// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// This file contains the definitions of TDI address objects and related
// constants and structures.
//


#define ao_signature 0x20204F41  // 'AO  '.

#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.
//
typedef struct AddrObj {
#if DBG
    ulong ao_sig;
#endif
    struct AddrObj *ao_next;           // Next address object in chain.
    KSPIN_LOCK ao_lock;                // Lock for this object.
    struct AORequest *ao_request;      // Pointer to pending request.
    Queue ao_sendq;                    // Queue of sends waiting for transmit.
    Queue ao_pendq;                    // Linkage for pending queue.
    Queue ao_rcvq;                     // Receive queue.
    IPv6Addr ao_addr;                  // IP address for this address object.
    ulong ao_scope_id;                 // Scope ID of IP address (0 if none).
    ushort ao_port;                    // Local port for this address object.
    uchar ao_prot;                     // Protocol for this AO.
    uchar ao_index;                    // Index into table of this AO.
    ulong ao_flags;                    // Flags for this object.
    uint ao_listencnt;                 // Number of listening connections.
    ushort ao_usecnt;                  // Count of 'uses' on AO.
    ushort ao_inst;                    // 'Instance' number of this AO.
    int ao_ucast_hops;                 // Hop count for unicast packets.
    uint ao_mcast_if;                  // Our multicast source interface.
    int ao_mcast_hops;                 // Hop count for multicast packets.
    int ao_mcast_loop;                 // The multicast loopback state.
    Queue ao_activeq;                  // Queue of active connections.
    Queue ao_idleq;                    // Queue of inactive (no TCB) connctns.
    Queue ao_listenq;                  // Queue of listening connections.
    WORK_QUEUE_ITEM ao_workitem;       // Work-queue item 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.
    PTDI_IND_ERROR_EX  ao_errorex;     // Error event routine.
    PVOID ao_errorexcontext;           // Error event context.
    ushort ao_maxdgsize;               // maximum user datagram size.
    ushort ao_udp_cksum_cover;         // UDP-Lite checksum coverage.
    ulong ao_owningpid;                // Process ID of owner.
    uint* ao_iflist;                   // List of enabled interfaces.
} AddrObj;

#define AO_RCV_HOPLIMIT_FLAG 0x00020000 // Receive HopLimit in aux data.
//                           0x00010000    Reserved for AO_WINSET_FLAG.
//                           0x00008000    Reserved for AO_SCALE_CWIN_FLAG.
//                           0x00004000    Reserved for AO_DEFERRED_FLAG.
#define AO_PKTINFO_FLAG      0x00002000 // Packet info structure passed in
                                        // control info (WSARecvMsg).
#define AO_SHARE_FLAG        0x00001000 // AddrObj can be shared.
//                           0x00000800    Reserved for AO_CONNUDP_FLAG.
#define AO_HDRINCL_FLAG      0x00000400 // User provides IP header on raw send.
#define AO_RAW_FLAG          0x00000200 // AO is for a raw endpoint.
#define AO_DHCP_FLAG         0x00000100 // AO is bound to real 0 address.

#define AO_VALID_FLAG        0x00000080 // AddrObj is valid.
#define AO_BUSY_FLAG         0x00000040 // AddrObj is busy (i.e., has it 
                                        // exclusive).
#define AO_OOR_FLAG          0x00000020 // AddrObj is out of resources, and on
                                        // either the pending or delayed queue.
#define AO_QUEUED_FLAG       0x00000010 // AddrObj is on the pending queue.

//                           0x00000008    Reserved for AO_XSUM_FLAG.
#define AO_SEND_FLAG         0x00000004 // Send is pending.
#define AO_OPTIONS_FLAG      0x00000002 // Option set pending.
#define AO_DELETE_FLAG       0x00000001 // 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_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))

#define AO_SHARE(A)  ((A)->ao_flags & AO_SHARE_FLAG)
#define SET_AO_SHARE(A) (A)->ao_flags |= AO_SHARE_FLAG
#define CLEAR_AO_SHARE(A) (A)->ao_flags &= ~AO_SHARE_FLAG

#define AO_HDRINCL(A)  ((A)->ao_flags & AO_HDRINCL_FLAG)
#define SET_AO_HDRINCL(A) (A)->ao_flags |= AO_HDRINCL_FLAG
#define CLEAR_AO_HDRINCL(A) (A)->ao_flags &= ~AO_HDRINCL_FLAG

#define AO_PKTINFO(A)  ((A)->ao_flags & AO_PKTINFO_FLAG)
#define SET_AO_PKTINFO(A) (A)->ao_flags |= AO_PKTINFO_FLAG
#define CLEAR_AO_PKTINFO(A) (A)->ao_flags &= ~AO_PKTINFO_FLAG

#define AO_RCV_HOPLIMIT(A)  ((A)->ao_flags & AO_RCV_HOPLIMIT_FLAG)
#define SET_AO_RCV_HOPLIMIT(A) (A)->ao_flags |= AO_RCV_HOPLIMIT_FLAG
#define CLEAR_AO_RCV_HOPLIMIT(A) (A)->ao_flags &= ~AO_RCV_HOPLIMIT_FLAG

//
// Definition of an address object search context.  This is a data structure
// used when the address object table is to be read sequentially.
//
typedef struct AOSearchContext {
    AddrObj *asc_previous;  // Previous AO found.
    IPv6Addr asc_addr;      // IP address to be found.
    uint asc_scope_id;      // Scope id for IP address.
    ushort asc_port;        // Port to be found.
    uchar asc_prot;         // Protocol.
    uchar asc_pad;          // Pad to dword boundary.
} AOSearchContext;

//
// Definition of an AO request structure.  There structures are used only for
// queuing delete and option set requests.
//
#define aor_signature 0x20524F41

typedef struct AORequest {
#if DBG
    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.
    RequestCompleteRoutine aor_rtn;  // Complete routine for this request.
    PVOID aor_context;               // Request context;
} AORequest;

typedef struct AOMCastAddr {
    struct AOMCastAddr *ama_next;  // Next in list.
    IPv6Addr ama_addr;             // The address.
    uint ama_if;                   // The interface.
} AOMCastAddr;


//
// External declarations for exported functions.
//
extern uint AddrObjTableSize;
extern AddrObj **AddrObjTable;
extern AddrObj *GetAddrObj(IPv6Addr *LocalAddr, uint LocalScopeId,
                           ushort LocalPort, uchar Prot, AddrObj *PreviousAO,
                           Interface* IF);
extern AddrObj *GetNextAddrObj(AOSearchContext *SearchContext);
extern AddrObj *GetFirstAddrObj(IPv6Addr *LocalAddr, uint LocalScopeId,
                                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,
                        IPv6Addr *Addr, ulong *ScopeId, ushort *Port);
extern int InitAddr(void);
extern void AddrUnload(void);
extern void ProcessAORequests(AddrObj *RequestAO);
extern void DelayDerefAO(AddrObj *RequestAO);
extern void DerefAO(AddrObj *RequestAO);
extern void FreeAORequest(AORequest *FreedRequest);
extern uint ValidateAOContext(void *Context, uint *Valid);
extern uint ReadNextAO(void *Context, void *OutBuf);
extern void InvalidateAddrs(IPv6Addr *Addr, uint ScopeId);

extern uint MCastAddrOnAO(AddrObj *AO, IPv6Addr *Addr);
extern AOMCastAddr *FindAOMCastAddr(AddrObj *AO, IPv6Addr *Addr, uint IFNo, AOMCastAddr **PrevAMA, BOOLEAN Loose);

#define GetBestAddrObj(addr, scope, port, prot, if) \
            GetAddrObj(addr, scope, port, prot, NULL, if)

#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)); \
        ExQueueWorkItem(&(a)->ao_workitem, CriticalWorkQueue); \
    }