//+++2004-08-31 // Copyright (C) 2004 Mike Rieker, Beverly, MA USA // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //---2004-08-31 /************************************************************************/ /* */ /* TCP/UDP/ICMP/IP driver */ /* */ /************************************************************************/ #include "ozone.h" #include "oz_dev_ipdns.h" #include "oz_dev_timer.h" #include "oz_io_ether.h" #include "oz_io_gen.h" #include "oz_io_ip.h" #include "oz_knl_cache.h" #include "oz_knl_devio.h" #include "oz_knl_event.h" #include "oz_knl_kmalloc.h" #include "oz_knl_log.h" #include "oz_knl_phymem.h" #include "oz_knl_printk.h" #include "oz_knl_process.h" #include "oz_knl_sdata.h" #include "oz_knl_section.h" #include "oz_knl_shuthand.h" #include "oz_knl_status.h" #include "oz_knl_thread.h" #include "oz_sys_dateconv.h" #define THREADID(iopex) oz_knl_thread_getid (oz_knl_ioop_getthread ((iopex) -> ioop)) // get request's thread-id #define ACCESSBUFFS(iopex) oz_knl_process_setcur (oz_knl_ioop_getprocess ((iopex) -> ioop)) // access request's buffers #define ACCESSBUFFS_OFF oz_knl_process_setcur (oz_s_systemproc) // turn off buffer access // should be called before // oz_knl_iodone in case the // request being posted aborts // the process out #define TDP if (iopex -> u.tcptransmit.p.debugme) oz_knl_printk // TCP-debug-print #define DEFWINSIZE (1460*3) #define MINWINSIZE 512 #define MINMTU 576 #define IPHDRSIZE (20) #define TCPHDRSIZE (IPHDRSIZE+20) /* Short synonyms for stuff in oz_io_ether.h and oz_io_ip.h */ #define IPADDRSIZE OZ_IO_IP_ADDRSIZE /* number of bytes for IP addresses */ #define DATASIZE OZ_IO_ETHER_MAXDATA /* max size an ethernet controller can send or receive */ #define PORTSIZE OZ_IO_IP_PORTSIZE /* number of bytes in a UDP or TCP port number */ typedef uLong Ipad; /* a type that can hold an IP address */ typedef uWord Sock; /* a type than can hold a socket number */ typedef uWord Eproto; /* a type that can hold ethernet protocol number */ #define ARPPKT(__hw, __cxb) ((Arppkt *)(__cxb + __hw -> ether_getinfo1.dataoffset)) /* where the arp packet is within the cxb */ #define IPPKT(__hw, __cxb) ((Ippkt *)(__cxb + __hw -> ether_getinfo1.dataoffset)) /* where the ip packet is within the cxb */ #define NUM_BUFFS_RCV_ARP (3) /* number of ARP receive buffers per interface */ #define NUM_BUFFS_RCV_IP (10) /* number of IP receive buffers per interface */ #define NUM_BUFFS_SNDU (12) /* number of user send buffers per interface */ /* Buffer types */ typedef enum { BUF_TYPE_SNDK = 1, /* internally originating buffers - there is an 'unlimited' supply of these */ BUF_TYPE_SNDU = 2, /* externally originating buffers - there is a limited supply of these */ BUF_TYPE_RCV_ARP = 3, /* ARP receive buffers */ BUF_TYPE_RCV_IP = 4, /* IP receive buffers */ BUF_TYPE_RCV_IPX = 5, /* extended IP receive buffer (No! It doesn't work with NetWare!) */ } Buftype; /* Timer resolution (ticks per second) and milliseconds per tick */ #define TPS 10 #define MSPT (1000/TPS) /* Time to delay sending a lone ack anticipating that we may be able */ /* to piggyback it on some data that we are about to send anyway */ #define ACKTIME 1 /* Debugging flag values */ #define debug oz_dev_ip_debug #define DEBUG_FAILRATE 0x000000F #define DEBUG_ARPCACHE 0x0000010 #define DEBUG_ETHERHDR 0x0000020 #define DEBUG_ARPPKT 0x0000040 #define DEBUG_IPHDR 0x0000080 #define DEBUG_ICMPHDR 0x0000100 #define DEBUG_UDPHDR 0x0000200 #define DEBUG_TCPHDR 0x0000400 #define DEBUG_ERRORS 0x0000800 #define DEBUG_TCPXMTQ 0x0004000 #define DEBUG_RECEIVE 0x0008000 #define DEBUG_TCPSUM 0x0010000 #define DEBUG_FORWARD 0x0020000 #define DEBUG_TCPFLOW 0x0040000 #define DEBUG_TCPRECV 0x0100000 #define DEBUG_THREAD 0x0200000 #define DEBUG_TCPTERM 0x0400000 #define DEBUG_BOUNCE 0x0800000 #define DEBUG_IOPOST 0x1000000 /* Host byte order <-> Network byte order conversions */ #define H2NW(h,n) { (n)[0] = (h) >> 8; (n)[1] = (h) & 0xff; } #define H2NL(h,n) { (n)[0] = (h) >> 24; (n)[1] = (h) >> 16; (n)[2] = (h) >> 8; (n)[3] = (h); } #define N2HW(n) (((n)[0] << 8) | (n)[1]) #define N2HL(n) (((n)[0] << 24) | ((n)[1] << 16) | ((n)[2] << 8) | (n)[3]) /* Ethernet protocol numbers */ #define PROTO_ARP 0x0806 #define PROTO_IP 0x0800 /* ARP related stuff */ #define ARP_OP_REQ 1 /* arp request */ #define ARP_OP_RPL 2 /* arp reply */ #define ARP_RETRY (TPS / 3) /* number of ticks between retries */ #define ARP_REQ_LIFE (15 * TPS) /* number of ticks to wait for a */ /* reply before aborting the transmit */ #define ARPCACHE_LIFE (60 * TPS) /* number of ticks to */ /* keep an arpcache entry */ /* IP related stuff */ #define PROTO_IP_ICMP 1 /* ICMP message type */ #define PROTO_IP_IGMP 2 /* IGMP message type (not used) */ #define PROTO_IP_TCP 6 /* TCP message type */ #define PROTO_IP_UDP 17 /* UDP message type */ #define IP_FLAGS_CE 0x8000 /* congestion */ #define IP_FLAGS_DF 0x4000 /* don't fragment packet */ #define IP_FLAGS_MF 0x2000 /* more fragments follow */ #define IP_FLAGS_OFFS 0x1FFF /* fragment offset */ #define IP_FLAGS_SHFT 3 /* fragment offset shift */ #define IP_FRAG_LIFE (3 * TPS) /* fragment lifetime (ticks) */ /* ICMP related stuff */ #define PROTO_IP_ICMP_PINGRPL 0 /* ping reply message */ #define PROTO_IP_ICMP_DESTUNR 3 /* destination unreachable */ #define PROTO_IP_ICMP_SQUENCH 4 /* source quench */ #define PROTO_IP_ICMP_REDIRECT 5 /* re-direct */ #define PROTO_IP_ICMP_PINGREQ 8 /* ping request message */ #define PROTO_IP_ICMP_TIMEEXC 11 /* time-to-live exceeded message */ #define PROTO_IP_ICMP_PRMPROB 12 /* parameter problem message */ #define PROTO_IP_ICMP_TIMEREQ 13 /* current system time request */ #define PROTO_IP_ICMP_TIMERPL 14 /* current system time reply */ #define ICMP_BOUNCE_TOTALEN (576) /* maximum total length on an ICMP bounce */ #define ICMP_BOUNCE_TTL (127) /* ICMP bounce message time-to-live */ #define ICMP_BOUNCE_INTERVAL (1) /* wait this many ticks between sending */ /* UDP and TCP related stuff */ #define EPHEMMIN 5000 /* lowest ephemeral socket number (inclusive) */ #define EPHEMMAX 6000 /* highest ephemeral socket number (exclusive) */ /* TCP related stuff */ #define TCP_FLAGS_FIN 0x0001 /* sender is finished sending data */ #define TCP_FLAGS_SYN 0x0002 /* synchronize sequence numbers */ #define TCP_FLAGS_RST 0x0004 /* reset the connection */ #define TCP_FLAGS_PSH 0x0008 /* push data to application asap */ #define TCP_FLAGS_ACK 0x0010 /* the acknowledgment number is valid */ #define TCP_FLAGS_URG 0x0020 /* the urgent pointer is valid */ #define TCP_FLAGS_HLNMSK 0xF000 /* header length (in uLongs) */ #define TCP_FLAGS_HLNSHF 12 #define TCP_FLAGX_KA 0x00010000 /* internal flag - keepalive being sent */ #define TCP_OPT_END 0 /* tcp option - end of list */ #define TCP_OPT_NOP 1 /* tcp option - no-op (padding) */ #define TCP_OPT_MSS 2 /* tcp option - max segment size */ #define TCP_MAXSENDSIZE(__hw) ((uByte *)IPPKT (__hw, wscxb) - IPPKT (__hw, wscxb) -> dat.tcp.raw + __hw -> mtu) /* Miscellaneous macros */ #define CLRIPAD(d) *(Ipad *)(d) = 0 /* clear an ip address */ #define ZERIPAD(d) (*(Ipad *)(d) == 0) /* test ip address for zero */ #define CEQIPAD(x,y) (*(Ipad *)(x) == *(Ipad *)(y)) /* compare ip addresses */ #define CGTIPAD(x,y) (memcmp (x, y, IPADDRSIZE) > 0) /* compare ip addresses */ #define CEQIPADM(x,y,m) (((*(Ipad *)(x) ^ *(Ipad *)(y)) & *(Ipad *)(m)) == 0) /* compare ip addresses with mask */ #define CPYIPAD(d,s) *(Ipad *)(d) = *(Ipad *)(s) /* copy ip address */ #define CPYIPADM(d,s,m) *(Ipad *)(d) = *(Ipad *)(s) & *(Ipad *)(m) /* copy ip address and mask it */ #define CLRPORT(d) *(Sock *)(d) = 0 /* clear a port number */ #define ZERPORT(d) (*(Sock *)(d) == 0) /* test port number for zero */ #define CEQPORT(x,y) (*(Sock *)(x) == *(Sock *)(y)) /* compare port numbers */ #define CPYPORT(d,s) *(Sock *)(d) = *(Sock *)(s) /* copy a port number */ #define CPYENAD(d,s,hw) memcpy (d, s, hw -> ether_getinfo1.addrsize) /* copy ethernet address */ #define CEQENAD(d,s,hw) (memcmp (d, s, hw -> ether_getinfo1.addrsize) == 0) /* compare ethernet address */ #define memclr(b,l) memset ((b), 0, (l)) /* printf - diagnostic print routine */ #define PRINTF oz_knl_log_print (oz_dev_ip_log, __FILE__, __LINE__, /* Struct typedefs */ typedef struct Filter Filter; typedef struct Hwipam Hwipam; typedef struct Hw Hw; typedef struct Arpc Arpc; typedef struct Route Route; typedef struct Buf Buf; typedef struct Tcpcon Tcpcon; typedef struct Msubp Msubp; typedef struct Abort Abort; typedef struct Chnex Chnex; typedef struct Iopex Iopex; /************************************************************************/ /* */ /* Message formats */ /* */ /************************************************************************/ /* Format of an ICMP packet */ #define Icmppkt OZ_IO_ip_icmppkt /* Format of an UDP packet */ #define Udppkt OZ_IO_ip_udppkt /* Format of a TCP packet */ #define Tcppkt OZ_IO_ip_tcppkt /* Format of an IP packet */ #define Ippkt OZ_IO_ip_ippkt /* Format of an ARP packet */ typedef struct { uByte hardtype[2]; // hardware address type uByte prottype[2]; // protocol address type uByte hardsize; // hardware address size uByte protsize; // protocol address size uByte op[2]; // opcode uByte var[(OZ_IO_ETHER_MAXADDR+IPADDRSIZE)*2]; // sender's ethernet address // sender's ip address // target's ethernet address // target's ip address } Arppkt; /************************************************************************/ /* */ /* Internal structures */ /* */ /************************************************************************/ /* Packet filters */ typedef struct { uLong data; uLong mask; } Filtera; struct Filter { Filter *next; uLong action; int acount; int astart; Filtera a[1]; }; /* Requests waiting for an user buffer */ struct Msubp { Msubp *next; Msubp **prev; void (*entry) (Buf *buf, void *param); void *param; }; /* Hardware interface struct */ struct Hwipam { Hwipam *next; uByte hwipaddr[IPADDRSIZE]; /* ip address of the hardware interface */ uByte nwipaddr[IPADDRSIZE]; /* network's ip address */ uByte nwipmask[IPADDRSIZE]; /* network's ip mask */ }; struct Hw { Hw *next; /* next in 'hws' list */ const char *devname; /* devunit's name string pointer */ OZ_Iochan *iochan_arp; /* arp protocol I/O channel */ OZ_Iochan *iochan_ip; /* ip protocol I/O channel */ Hwipam *hwipams; /* list of my ip addresses and masks */ Arpc *arpcs; /* list of known ip <-> ethernet address pairs */ int arpcount; /* number of items on arpcs queue (debugging only) */ Buf *arpwaits; /* list of buf's waiting to transmit when arp cache entry is ready */ Msubp *waitsndubufh; /* list of requests waiting for user send buffer */ Msubp **waitsndubuft; int waitsndubufc; /* number of msubps on waitsndubufh (debugging only) */ Buf *freesndubufs; /* list of free user send buffers */ int freesndubufc; /* number of buffs on freesndubufs (debugging only) */ int terminated; /* set when it has been terminated */ Long receivesinprog; /* number of recieve i/o's in progress on ethernet device */ Long transmitsinprog; /* number of transmit i/o's in progress on ethernet device */ uLong lastbounce; /* tick that the last icmp bounce was sent */ OZ_IO_ether_getinfo1 ether_getinfo1; /* ethernet device's getinfo1 data */ uWord mtu; /* maximum message size (not incl en hdr and crc, including ip hdr and data) */ uByte myenaddr[OZ_IO_ETHER_MAXADDR]; /* ethernet address of the interface */ }; /* Arp Cache entries */ struct Arpc { Arpc *next; /* pointer to next in arpcs list */ uLong timeout; /* when this entry expires (ticks) */ /* - it is deleted from arpcs list */ uByte ipaddr[IPADDRSIZE]; /* ip address */ uByte enaddr[OZ_IO_ETHER_MAXADDR]; /* ethernet address */ }; /* Routing table entries */ struct Route { Route *next; uByte gwipaddr[IPADDRSIZE]; /* gateway's ip address */ uByte nwipaddr[IPADDRSIZE]; /* network's ip address */ uByte nwipmask[IPADDRSIZE]; /* network's ip mask */ Hw *hw; /* hardware interface that the router is connected to */ /* (or NULL if not currently reachable) */ uByte hwipaddr[IPADDRSIZE]; /* the corresponding hwipaddr that it is connected to */ }; /* Ethernet packet messages */ #define Cxb uByte /* General send / receive buffer */ struct Buf { Cxb *cxb; /* I/O buffer pointer */ uLong iosts; /* I/O status */ uLong cxb_dlen; /* length of data in cxb */ Buftype type; /* buffer type (BUF_TYPE_...) */ Hw *hw; /* hardware interface */ Buf *next; /* next in list - frags, arpwaits, rcvdoutofordr */ Buf *frag; /* next fragment - sorted by ascending fragment offset */ uLong timeout; /* when this entry expires (ticks) - */ /* - frags : all fragments are freed off */ /* - arpwaits : a new arp request is sent */ /* - trans... : the buffer is re-sent */ /* - recei... : a lone ack is sent */ uLong tcprawlen; /* length of usable tcp data */ uByte *tcprawpnt; /* pointer to usable tcp data within cxb */ uLong tcprawseq; /* sequence number of byte that tcprawpnt points to */ void *rcvdrv; /* if receive type, pointer to ethernet driver's internal buffer */ void *xmtdrv; /* if transmit type, pointer to ethernet driver's internal buffer */ void (*donentry) (void *donparam, uLong status); /* NULL: do nothing when transmit completes */ /* else: call routine when transmit completes with status */ void *donparam; /* param to pass to xmtdonentry routine */ int tcphasfin; /* 0: tcp message had TCP_FLAGS_FIN clear */ /* 1: tcp message had TCP_FLAGS_FIN set */ uByte sendipaddr[IPADDRSIZE]; /* ip address to send to (might be router, not necessarily the one in cxb->dstipaddr) */ }; /* TCP connection context block */ struct Tcpcon { Tcpcon *next; /* next in tcpcons list */ Chnex *rcvchnex; /* receive data channel (or NULL if none - it is being closed down) */ uLong windowsize; /* window size from connect or listen request */ uByte *windowbuff; /* window buffer from connect or listen request */ int window99s; /* window will be filled with 0x99s */ OZ_Seclock *windowlock; /* keeps windowbuff locked en memorie */ OZ_Process *windowprocess; /* process the thread was mapped to at the time */ uLong rcvwindowrem; /* offset in windowbuff to remove data */ /* - ie, offset of start of valid contiguous data */ /* - data at or after this offset (up to rcvwindowins) are */ /* protected from being overwritten by new incoming data */ /* - range: 0..windowsize-1 */ uLong rcvwindownxt; /* offset in windowbuff for next receive */ /* - range: rcvwindowrem..rcvwindowins */ uLong rcvwindowins; /* offset in windowbuff to insert data */ /* - data byte at this offset has sequence number seq_receivedok */ /* - data at or after this offset can be overwritten by incoming data */ /* - range: rcvwindowrem..rcvwindowrem+windowsize */ uLong seq_lastacksent; /* last 'ack' sequence number transmitted */ /* - not valid until rcvdsyn is set */ uLong seq_lastacksent2; /* - and the one sent just beofre that */ uLong seq_receivedok; /* seq of the next contiguous byte to be received */ /* - ie, seq number of byte pointed to by rcvwindowins */ /* - not valid until rcvdsyn is set */ uLong seq_lastrcvdack; /* last received ack sequence number */ uLong seq_lastrcvdwsize; /* seq number that remote end is allowing us to send up to */ uLong seq_nextusersendata; /* seq number to assign to next user data queued */ uLong smoothedroundtrip; /* smoothed round trip time (ticks * 8) */ uLong roundtripdeviation; /* round trip deviations (ticks * 8) */ uLong retransmitinterval; /* resultant retransmit interval (ticks) */ uLong lasttimemsgsent; /* last time a message was sent */ uLong lasttimemsgrcvd; /* last time a message was received */ uLong lasttimenzwsizercvd; /* last time msg rcvd with non-zero wsize */ uLong slowstarthresh; /* slow-start threshold */ /* - when we have transmitted more than this amount, do congestion avoidance */ /* - until then, do slow-start processing */ /* - it initially contains 65535 */ /* - when congestion occurs (timeout or duplicate acks), */ /* it gets set to congestionwindow / 2 */ /* - results in exponential increase to half the congestion point */ /* then linear increase from that point on */ uLong congestionwindow; /* congestion window */ /* - we only have at most this number of unacked bytes on network */ /* - slow start algorithm: */ /* - - it initially contains one segment worth (1460 bytes) */ /* - - it increments by one segment everytime other end acks a buf */ /* - - it resets to one segment if we get a retransmit timeout */ /* - - results in an exponential increase in rate until an error happens */ /* - congestion avoidance algorithm: */ /* - - it increments by 1/cwnd + ssize/8 everytime other end acks a buf */ /* - - results in an arithmetic increase in rate until an error happens */ int dupacksinarow; /* number of duplicate acks in a row */ Buf *rcvdoutofordr; /* queue of buffers received out of sequence */ uByte lipaddr[IPADDRSIZE]; /* local (this) host's ip address */ uByte ripaddr[IPADDRSIZE]; /* remote host's ip address */ Sock lsockno; /* local (this) host's socket number */ Sock rsockno; /* remote host's socket number */ uLong timeout; /* when to timeout connection attempt (ticks) */ uLong lastwsizesent; /* last window size sent */ uLong maxsendsize; /* max segment size to send */ uLong nextxmtseq; /* sequence of next byte in nextackreq list that needs to be transmitted */ Iopex *nextackreq; /* next request that has data that needs to be acked */ Iopex **lastxmtreq; /* last request in nextackreq chain */ uLong numxmtreqs; /* number of requests on nextackreq list (debug only) */ uLong expressreceive; /* number of packets processed by express routine */ uLong normalreceive; /* number of packets processed by normal routine */ uLong sendpend; /* number of ip packets whose transmission is in progress */ Iopex *lisiopex; /* listening I/O request */ Iopex *tcprcv_iopqh; /* list of pending receive requests */ Iopex **tcprcv_iopqt; Iopex *poll_iopqh; /* list of pending poll requests */ Iopex **poll_iopqt; Msubp msubp; /* used to wait for user buffers */ uLong reset; /* 0 : hasn't been reset */ /* else : reset error status */ char sentsyn; /* 0 : haven't sent TCP_FLAGS_SYN yet */ /* 1 : have sent TCP_FLAGS_SYN */ char rcvdsyn; /* 0 : haven't received TCP_FLAGS_SYN yet */ /* 1 : have received TCP_FLAGS_SYN */ char sentfin; /* 0 : haven't sent TCP_FLAGS_FIN */ /* 1 : have sent TCP_FLAGS_FIN */ char rcvdfin; /* 0 : haven't rcvd TCP_FLAGS_FIN */ /* 1 : have rcvd TCP_FLAGS_FIN */ char tcpclosed; /* 0 : be_tcpclose hasn't been called yet */ /* 1 : be_tcpclose has been called */ char sentrst; /* set when tcpterminate has sent a TCP_FLAGS_RST message */ }; /* Put on abortq to abort the I/O on a channel */ struct Abort { Abort *next; /* next in abortq */ int deassign; /* 0: ip_abort call, 1: ip_deassign call */ OZ_Iochan *iochan; /* iochan being aborted */ OZ_Procmode procmode; /* procmode of the abort */ OZ_Ioop *ioop; /* ioop being aborted */ Chnex *chnex; /* chnex being aborted */ }; /* I/O driver data definitions */ struct Chnex { OZ_Iochan *iochan; /* corresponding I/O channel */ Chnex *ipbind_next; /* next in 'ipbinds' list */ uByte ipbind_lclipaddr[IPADDRSIZE]; /* local IP address */ uByte ipbind_remipaddr[IPADDRSIZE]; /* remote IP address */ Iopex *ipbind_iopqh, **ipbind_iopqt; /* list of pending receive requests */ uLong ip_transcount; /* number of transmits queued */ uLong ip_recvcount; /* number of receives queued */ OZ_Threadid ip_threadid; /* thread id of last io */ uByte ipbind_proto; /* ip protocol number */ char ipbind_passiton; /* 1: pass IP packet on for normal processing */ char pad1[2]; Chnex *udpbind_next; /* next in 'udpbinds' list */ uByte udpbind_lclipaddr[IPADDRSIZE]; /* local IP address */ uByte udpbind_remipaddr[IPADDRSIZE]; /* remote IP address */ uByte udpbind_lclportno[PORTSIZE]; /* local port number */ uByte udpbind_remportno[PORTSIZE]; /* remote port number */ Iopex *udpbind_iopqh, **udpbind_iopqt; /* list of pending receive requests */ int udpbind_ephem; /* ephemeral socket number */ uLong udp_transcount; /* number of transmits queued */ uLong udp_recvcount; /* number of receives queued */ OZ_Threadid udp_threadid; /* thread id of last io */ Tcpcon *tcpcon; /* tcp connection context */ uLong tcp_transcount; /* number of transmits queued */ uLong tcp_recvcount; /* number of receives queued */ OZ_Threadid tcp_threadid; /* thread id of last io */ }; struct Iopex { Iopex *next; /* general purpose 'next iopex' pointer */ OZ_Ioop *ioop; /* corresponding ioop pointer */ OZ_Procmode procmode; Chnex *chnex; /* corresponding chnex pointer */ uLong (*be) (Iopex *iopex); /* back-end processing routine */ Msubp msubp; /* used when we get hung waiting for a user transmit buffer */ union { struct { OZ_IO_gen_poll p; } genpoll; struct { OZ_IO_ip_hwadd p; char devname[OZ_DEVUNIT_NAMESIZE]; } hwadd; struct { OZ_IO_ip_hwrem p; char devname[OZ_DEVUNIT_NAMESIZE]; } hwrem; struct { OZ_IO_ip_hwipamadd p; char devname[OZ_DEVUNIT_NAMESIZE]; uByte hwipaddr[IPADDRSIZE]; uByte nwipaddr[IPADDRSIZE]; uByte nwipmask[IPADDRSIZE]; } hwipamadd; struct { OZ_IO_ip_hwipamrem p; char devname[OZ_DEVUNIT_NAMESIZE]; uByte hwipaddr[IPADDRSIZE]; uByte nwipaddr[IPADDRSIZE]; } hwipamrem; struct { OZ_IO_ip_hwlist p; } hwlist; struct { OZ_IO_ip_hwipamlist p; char devname[OZ_DEVUNIT_NAMESIZE]; } hwipamlist; struct { OZ_IO_ip_arpadd p; char devname[OZ_DEVUNIT_NAMESIZE]; uByte ipaddr[IPADDRSIZE]; uByte enaddr[OZ_IO_ETHER_MAXADDR]; } arpadd; struct { OZ_IO_ip_arplist p; char devname[OZ_DEVUNIT_NAMESIZE]; } arplist; struct { OZ_IO_ip_routeadd p; uByte gwipaddr[IPADDRSIZE]; uByte nwipaddr[IPADDRSIZE]; uByte nwipmask[IPADDRSIZE]; } routeadd; struct { OZ_IO_ip_routerem p; uByte gwipaddr[IPADDRSIZE]; uByte nwipaddr[IPADDRSIZE]; uByte nwipmask[IPADDRSIZE]; } routerem; struct { OZ_IO_ip_routelist p; } routelist; struct { OZ_IO_ip_filteradd p; } filteradd; struct { OZ_IO_ip_filterrem p; } filterrem; struct { OZ_IO_ip_filterlist p; } filterlist; struct { OZ_IO_ip_ipbind p; uByte lclipaddr[IPADDRSIZE]; uByte remipaddr[IPADDRSIZE]; } ipbind; struct { OZ_IO_ip_iptransmit p; } iptransmit; struct { OZ_IO_ip_ipreceive p; Buf *buf; } ipreceive; struct { OZ_IO_ip_ipgetinfo1 p; uLong as; void *ap; } ipgetinfo1; struct { OZ_IO_ip_udpbind p; uByte lclipaddr[IPADDRSIZE]; uByte lclportno[PORTSIZE]; uByte remipaddr[IPADDRSIZE]; uByte remportno[PORTSIZE]; } udpbind; struct { OZ_IO_ip_udptransmit p; uByte myipaddr[IPADDRSIZE]; uWord ident; } udptransmit; struct { OZ_IO_ip_udpreceive p; Buf *buf; } udpreceive; struct { OZ_IO_ip_udpgetinfo1 p; uLong as; void *ap; } udpgetinfo1; struct { OZ_IO_ip_tcpconnect p; } tcpconnect; struct { OZ_IO_ip_tcplisten p; Iopex **prev; Tcpcon *tcpcon; } tcplisten; struct { OZ_IO_ip_tcpreceive p; } tcpreceive; struct { OZ_IO_ip_tcptransmit p; uLong startseq; uLong sendstarted; uLong lasttimesent; uLong retransmits; uWord syn; } tcptransmit; struct { OZ_IO_ip_tcpclose p; } tcpclose; struct { OZ_IO_ip_tcpgetinfo1 p; uLong as; void *ap; } tcpgetinfo1; struct { OZ_IO_ip_tcpwindow p; } tcpwindow; struct { OZ_IO_ip_dnslookup p; uLong numel; uByte *array; char *name; } dnslookup; } u; /* function dependent data */ }; static uLong ip_assign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode); static int ip_deassign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv); static void ip_abort (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Ioop *ioop, void *iopexv, OZ_Procmode procmode); static uLong ip_start (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode, OZ_Ioop *ioop, void *iopexv, uLong funcode, uLong as, void *ap); static const OZ_Devfunc functable = { 0, sizeof (Chnex), sizeof (Iopex), 0, NULL, NULL, NULL, ip_assign, ip_deassign, ip_abort, ip_start, NULL }; static int initialized = 0; static OZ_Devclass *devclass; static OZ_Devdriver *devdriver; static OZ_Devunit *devunit; /* Static data */ static const Byte hextab[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const OZ_Datebin tickdelta = OZ_TIMER_RESOLUTION / TPS; /* delta time for a clock tick */ static const uByte nullipaddr[IPADDRSIZE] = { -1,-1,-1,-1 }; /* null ip address */ static uByte arpenaddr[OZ_IO_ETHER_MAXADDR] = { -1,-1,-1,-1,-1,-1 }; /* arp ethernet broadcast address */ uLong oz_dev_ip_debug = 0; /* debug enable flags */ OZ_Log *oz_dev_ip_log = NULL; static Filter *input_filters = NULL; static Filter *forward_filters = NULL; static Filter *output_filters = NULL; static Abort *abortqh = NULL; /* list of pending abort requests */ static Abort **abortqt = &abortqh; static Buf *frags = NULL; /* list of incomplete incoming fragmented ip buffers */ static Buf *rcvqh = NULL; /* list of buffers that have been received */ static Buf **rcvqt = &rcvqh; static Buf *xmtqh = NULL; /* list of buffers that have been transmitted */ static Buf **xmtqt = &xmtqh; static Chnex *ipbinds = NULL; /* I/O channels that have current ipbinds on them */ static Chnex *udpbinds = NULL; /* I/O channels that have current udpbinds on them */ static Cxb *wscxb = NULL; /* just used for TCP_MAXSENDSIZE */ static Hw *hws = NULL; /* hardware interfaces */ static int scanmsubps = 0; /* set to scan hw's for requests waiting for a free transmit buffer */ static int tcp_ephemnext = EPHEMMIN; /* next ephemeral socket to use */ static int udp_ephemnext = EPHEMMIN; /* next ephemeral socket to use */ static Iopex *iopqh = NULL; /* list of pending i/o requests */ static Iopex **iopqt = &iopqh; static OZ_Datebin tickdatebin; /* time of next tick */ static OZ_Event *shuttingdownflag = NULL; /* set when shutting down */ static OZ_Thread *ipthread = NULL; /* kernel thread to process ip requests */ static OZ_Event *ipevent = NULL; /* event flag to wait for ip requests */ static OZ_Smplock smplock_vl; /* lock for the queues */ static OZ_Timer *ticktimer = NULL; /* tick timer struct pointer */ static Route *routes = NULL; /* router entries */ static Tcpcon *tcpcons = NULL; /* list of active tcp connections */ static int ntcpcons = 0; /* number of tcpcons on tcpcons list (debugging only) */ static Iopex *tcplisqh = NULL; /* ports that tcp listening is enabled on */ static Iopex **tcplisqt = &tcplisqh; static uByte udp_ephemsocks[EPHEMMAX-EPHEMMIN]; /* ephemeral sockets */ static uLong tcp_ephemsocks[EPHEMMAX-EPHEMMIN]; /* ephemeral sockets */ static uLong ticks = 0; /* number clock ticks since startup (used by timeout fields) */ static uWord ident = 0; /* ident number for ip header when sending datagrams */ /* Internal subroutine prototypes */ static uLong kthentry (void *dummy); static void timerast (void *dummy, OZ_Timer *timer); static void shuttingdown (void *dummy); static uLong be_genpoll (Iopex *iopex); static uLong be_hwadd (Iopex *iopex); static uLong be_hwrem (Iopex *iopex); static uLong be_hwipamadd (Iopex *iopex); static uLong be_hwipamrem (Iopex *iopex); static uLong be_hwlist (Iopex *iopex); static uLong be_hwipamlist (Iopex *iopex); static int hwipamrem (Hw *hw, const uByte nwipaddr[IPADDRSIZE], const uByte hwipaddr[IPADDRSIZE]); static void hwterminate (Hw *hw); static void hwchanged (void); static uLong be_arpadd (Iopex *iopex); static uLong be_arplist (Iopex *iopex); static uLong be_routeadd (Iopex *iopex); static uLong be_routerem (Iopex *iopex); static uLong be_routelist (Iopex *iopex); static uLong be_filteradd (Iopex *iopex); static uLong be_filterrem (Iopex *iopex); static uLong be_filterlist (Iopex *iopex); static uLong be_ipbind (Iopex *iopex); static uLong be_iptransmit (Iopex *iopex); static void iptransmit_mem (Buf *buf, void *iopexv); static void iptransmit_done (void *iopexv, uLong status); static uLong be_ipreceive (Iopex *iopex); static uLong be_ipgetinfo1 (Iopex *iopex); static uLong be_udpbind (Iopex *iopex); static uLong be_udptransmit (Iopex *iopex); static void udptransmit_mem (Buf *buf, void *iopexv); static void udptransmit_done (void *iopexv, uLong status); static uLong be_udpreceive (Iopex *iopex); static uLong be_udpgetinfo1 (Iopex *iopex); static uLong be_tcpconnect (Iopex *iopex); static uLong be_tcpclose (Iopex *iopex); static uLong be_tcplisten (Iopex *iopex); static uLong be_tcptransmit (Iopex *iopex); static uLong be_tcpreceive (Iopex *iopex); static void tcpreceive_test (Tcpcon *tcpcon); static uLong be_tcpgetinfo1 (Iopex *iopex); static uLong be_tcpwindow (Iopex *iopex); static Sock alloctcpephem (uLong *seq_r); static void abortproc (Abort *abort); static void timerproc (void); static void startread (Buf *buf); static void readast (void *bufv, uLong status); static void arpreadproc (Buf *buf); static void ipreadproc (Buf *buf); static uWord proc_ether (Buf *buf); static Buf *proc_arp (Buf *buf); static Buf *proc_ip (Buf *buf); static Buf *proc_icmp (Buf *buf); static void printbounce (Ippkt *ip); static void proc_destunr (Icmppkt *icmppkt); static Buf *proc_udp (Buf *buf); static Buf *proc_tcp (Buf *buf); static void checkwin99s (uByte *buf, uLong siz, Tcpcon *tcpcon); static uLong tcpbegseq (Ippkt *ippkt); static uLong tcpendseq (Ippkt *ippkt); static uWord tcprawlen (Ippkt *ippkt); static void tcpreplyack (Tcpcon *tcpcon, int outofordr); static int checktcpdead (Tcpcon *tcpcon); static void queuetcptransmit (Iopex *iopex, Tcpcon *tcpcon); static int starttcpsend (Buf *buf, Tcpcon *tcpcon); static void starttcpsendagain (Buf *buf, void *tcpconv); static void validtcpxmtq (Tcpcon *tcpcon); static void sendtcp (Buf *buf, Tcpcon *tcpcon, uWord rawlen, uLong flags, uLong seq_receivedok); static void sendtcpdone (void *tcpconv, uLong status); static uLong tcpwindowsize (Tcpcon *tcpcon, uLong seq_receivedok); static void sendicmpbounce (Buf *buf, uByte icmptype, uByte icmpcode, uLong extra); static uLong sendip (Buf *buf, void (*donentry) (void *donparam, uLong status), void *donparam); static void sendippkt (Buf *buf, void (*donentry) (void *donparam, uLong status), void *donparam); static void sendarpreq (Hw *hw, const uByte hwipaddr[IPADDRSIZE], const uByte tipaddr[IPADDRSIZE]); static Hw *find_hw_by_name (const char *devname); static Hw *find_send_hw (const uByte *ipaddr, uByte *myipaddr, uByte *gwipaddr, int inclroutes); static int ismyipaddr (const uByte ipaddr[IPADDRSIZE]); static int isnwbcipad (const uByte ipaddr[IPADDRSIZE], Hw *hw); static void sendpkt (Buf *buf, uWord totalen, const uByte enaddr[OZ_IO_ETHER_MAXADDR]); static void writeast (void *bufv, uLong status); static void sendcomplete (Buf *buf); static Eproto geteproto (Buf *buf); static void seteproto (Eproto eproto, Buf *buf); static void addtoarpc (Hw *hw, const uByte enaddr[OZ_IO_ETHER_MAXADDR], const uByte ipaddr[IPADDRSIZE], uLong timeout); static int filter_check (Filter **filters, Ippkt *ippkt); static void tcpsum (char rt, Ippkt *ip); static void printippkt (char *indents, Ippkt *ip); static void printtcpcon (Tcpcon *tcpcon); static void printtcpflags (uWord flags); static void printenaddr (Hw *hw, const uByte enaddr[OZ_IO_ETHER_MAXADDR]); static void printipaddr (const uByte ipaddr[IPADDRSIZE]); static Arpc *mallocarpc (void); static void freearpc (Arpc *arpc); static uLong calloctcpcon (Iopex *iopex, uLong windowsize, uByte *windowbuff, Tcpcon **tcpcon_r); static void tcppolldone (Tcpcon *tcpcon, uLong mask); static void tcpterminate (Tcpcon *tcpcon); static Buf *mallocsndubuf (Hw *hw, void (*entry) (Buf *buf, void *param), void *param, Msubp *msubp); static Buf *mallocsndkbuf (Hw *hw); static void freecxb (Buf *oldbuf); static void freebuf (Buf *buf); static void valsndubufs (Hw *hw, int line); static int debugfailrate (void); static void validatearpcs (Hw *hw, int line); /************************************************************************/ /* */ /* Initialization routine */ /* */ /* Input: */ /* */ /* debugmsk = debug flags mask */ /* etherdevs = comma separaeted ethernet device name list */ /* ipaddr = ip addresses corresponding to etherdevs */ /* ipid = my process id */ /* rmod = access mode */ /* */ /************************************************************************/ void oz_dev_ip_init (void) { int i; uLong sts; if (initialized) return; oz_knl_printk ("oz_dev_ip_init\n"); memset (arpenaddr, -1, sizeof arpenaddr); /* Set up device driver definitions - we only have one devunit, "ip" */ if (devunit == NULL) { oz_hw_smplock_init (sizeof smplock_vl, &smplock_vl, OZ_SMPLOCK_LEVEL_VL); devclass = oz_knl_devclass_create (OZ_IO_IP_CLASSNAME, OZ_IO_IP_BASE, OZ_IO_IP_MASK, "oz_dev_ip"); devdriver = oz_knl_devdriver_create (devclass, "oz_dev_ip"); devunit = oz_knl_devunit_create (devdriver, OZ_IO_IP_DEV, "ip access device", &functable, 0, oz_s_secattr_tempdev); } /* Start thread, then init dns if successful */ sts = oz_knl_event_create (15, "oz_dev_ip event", NULL, &ipevent); if (sts != OZ_SUCCESS) oz_knl_printk ("oz_dev_ip_init: error %u creatting event flag\n", sts); else { sts = oz_knl_thread_create (oz_s_systemproc, oz_knl_user_getmaxbasepri (NULL), NULL, NULL, NULL, 0, kthentry, NULL, OZ_ASTMODE_INHIBIT, 16, "oz_dev_ip thread", NULL, &ipthread); if (sts != OZ_SUCCESS) oz_knl_printk ("oz_dev_ip_init: error %u starting kernel thread\n", sts); else { initialized = 1; oz_dev_ipdns_init (sizeof (Iopex)); oz_knl_thread_orphan (ipthread); } } } /************************************************************************/ /* */ /* An new channel was just assigned to the 'ip' device */ /* Clear out the chnex struct */ /* */ /************************************************************************/ static uLong ip_assign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode) { Chnex *chnex; chnex = chnexv; memset (chnex, 0, sizeof *chnex); OZ_KNL_CHKOBJTYPE (iochan, OZ_OBJTYPE_IOCHAN); chnex -> iochan = iochan; return (OZ_SUCCESS); } /************************************************************************/ /* */ /* An old channel is being deassigned from the 'ip' device */ /* Make sure everything is closed on the channel */ /* */ /* Input: */ /* */ /* devunit,devexv = device being deassigned from ("ip") */ /* iochan,chnexv = channel being deassigned */ /* smplevel = softint */ /* */ /* Output: */ /* */ /* ip_deassign = 0 : deassign complete, channel all cleared out */ /* 1 : come back later when ref count goes 0 again */ /* */ /************************************************************************/ static int ip_deassign (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv) { Abort *abort; Chnex *chnex; uLong vl; chnex = chnexv; /* If anything going on in channel, queue an abort for it */ /* Theoretically, nothing new can queue (as there are supposedly no references out on the iochan) */ if ((chnex -> ipbind_iopqt != NULL) /* make sure not on ipbinds list */ || (chnex -> udpbind_iopqt != NULL) /* make sure not on udpbinds list */ || (chnex -> tcpcon != NULL)) { /* make sure not connected or connecting */ abort = OZ_KNL_NPPMALLOC (sizeof *abort); /* something happening, queue it for abort */ abort -> next = NULL; abort -> deassign = 1; abort -> iochan = iochan; abort -> procmode = OZ_PROCMODE_KNL; abort -> ioop = NULL; abort -> chnex = chnex; oz_knl_iochan_increfc (iochan, 1); /* inc ref count, it gets decremented at end of abortproc */ vl = oz_hw_smplock_wait (&smplock_vl); *abortqt = abort; abortqt = &(abort -> next); oz_hw_smplock_clr (&smplock_vl, vl); /* release iopq and abortq */ oz_knl_event_set (ipevent, 1); /* wake kernel thread to abort whatever it has to abort */ return (1); /* come back when abortproc decs the iochan's ref count */ } return (0); /* nothing going on, ok to delete channel block */ } /************************************************************************/ /* */ /* Abort all applicable I/O on a given channel */ /* */ /* Input: */ /* */ /* devunit,devexv = device the I/O is being aborted on */ /* iochan,chnexv = channel the I/O is being aborted on */ /* ioop = NULL : abort all those I/O's */ /* else : abort only this I/O */ /* procmode = processor mode the I/O is being aborted for */ /* smplevel = softint */ /* */ /************************************************************************/ static void ip_abort (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Ioop *ioop, void *iopexv, OZ_Procmode procmode) { Abort *abort; int aborted; Iopex *iopex, *iopexs, **liopex; uLong vl; iopexs = NULL; /* haven't found anything to abort yet */ /* Remove everything from the iopq (I/O's waiting to be processed by the thread be_... routine) */ oz_knl_iochan_increfc (iochan, 1); /* inc ref count (gets dec'd at end of abortproc) */ vl = oz_hw_smplock_wait (&smplock_vl); /* lock iopq and abortq */ for (liopex = &iopqh; (iopex = *liopex) != NULL;) { /* scan it */ if (!oz_knl_ioabortok (iopex -> ioop, iochan, procmode, ioop)) liopex = &(iopex -> next); else { *liopex = iopex -> next; /* something abortable found, unlink it */ iopex -> next = iopexs; /* link it to list of stuff to abort */ iopexs = iopex; } } iopqt = liopex; /* fix end-of-queue pointer */ /* Put channel on abortq so kernel thread will abort anything applicable */ abort = OZ_KNL_NPPMALLOC (sizeof *abort); abort -> next = NULL; abort -> deassign = 0; abort -> iochan = iochan; abort -> procmode = procmode; abort -> ioop = ioop; abort -> chnex = chnexv; *abortqt = abort; abortqt = &(abort -> next); oz_hw_smplock_clr (&smplock_vl, vl); /* release iopq and abortq */ oz_knl_event_set (ipevent, 1); /* ... then wake kernel thread to abort */ /* Abort all the iopexs' that were found */ while ((iopex = iopexs) != NULL) { iopexs = iopex -> next; oz_knl_iodone (iopex -> ioop, OZ_ABORTED, NULL, NULL, NULL); } /* Find all DNS requests and kill them, too */ oz_dev_ipdns_abort (iochan, ioop, procmode); } /************************************************************************/ /* */ /* An new I/O request is being started */ /* */ /* Input: */ /* */ /* devunit,devexv = device the request is starting on ("ip") */ /* iochan,chnexv = channel the request is starting on */ /* procmode = processor mode the requestor is in */ /* ioop,iopexv = i/o op struct */ /* funcode = function code */ /* as,ap = argument size and pointer */ /* smplevel = softint */ /* */ /* Output: */ /* */ /* ip_start = OZ_STARTED : operation will complete via oz_knl_iodone /* else : completion status */ /* */ /************************************************************************/ static uLong ip_start (OZ_Devunit *devunit, void *devexv, OZ_Iochan *iochan, void *chnexv, OZ_Procmode procmode, OZ_Ioop *ioop, void *iopexv, uLong funcode, uLong as, void *ap) { Chnex *chnex; Iopex *iopex; uLong sts, vl; chnex = chnexv; iopex = iopexv; iopex -> ioop = ioop; iopex -> chnex = chnex; iopex -> be = NULL; iopex -> msubp.prev = NULL; iopex -> procmode = procmode; OZ_KNL_CHKOBJTYPE (chnex -> iochan, OZ_OBJTYPE_IOCHAN); switch (funcode) { /* Generic poll request */ case OZ_IO_GEN_POLL: { movc4 (as, ap, sizeof iopex -> u.genpoll.p, &(iopex -> u.genpoll.p)); iopex -> be = be_genpoll; break; } /* Add hardware interface */ case OZ_IO_IP_HWADD: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.hwadd.p, &(iopex -> u.hwadd.p)); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.hwadd.devname, iopex -> u.hwadd.p.devname, iopex -> u.hwadd.devname, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwadd; break; } /* Remove hardware interface */ case OZ_IO_IP_HWREM: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.hwrem.p, &(iopex -> u.hwrem.p)); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.hwrem.devname, iopex -> u.hwrem.p.devname, iopex -> u.hwrem.devname, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwrem; break; } /* Add hardware interface ip address and mask */ case OZ_IO_IP_HWIPAMADD: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.hwipamadd.p, &(iopex -> u.hwipamadd.p)); if (iopex -> u.hwipamadd.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.hwipamadd.devname, iopex -> u.hwipamadd.p.devname, iopex -> u.hwipamadd.devname, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.hwipamadd.p.hwipaddr, iopex -> u.hwipamadd.hwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.hwipamadd.p.nwipaddr, iopex -> u.hwipamadd.nwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.hwipamadd.p.nwipmask, iopex -> u.hwipamadd.nwipmask); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwipamadd; break; } /* Remove hardware interface ip address and mask */ case OZ_IO_IP_HWIPAMREM: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.hwipamrem.p, &(iopex -> u.hwipamrem.p)); if (iopex -> u.hwipamrem.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.hwipamrem.devname, iopex -> u.hwipamrem.p.devname, iopex -> u.hwipamrem.devname, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.hwipamrem.p.hwipaddr, iopex -> u.hwipamrem.hwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.hwipamrem.p.nwipaddr, iopex -> u.hwipamrem.nwipaddr); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwipamrem; break; } /* List hardware interfaces */ case OZ_IO_IP_HWLIST: { movc4 (as, ap, sizeof iopex -> u.hwlist.p, &(iopex -> u.hwlist.p)); if (iopex -> u.hwlist.p.devnamesize == 0) return (OZ_BADPARAM); sts = oz_knl_ioop_lockw (ioop, iopex -> u.hwlist.p.devnamesize, iopex -> u.hwlist.p.devnamebuff, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwlist; break; } /* List hardware interface ip addresses and masks */ case OZ_IO_IP_HWIPAMLIST: { movc4 (as, ap, sizeof iopex -> u.hwipamlist.p, &(iopex -> u.hwipamlist.p)); /* get arg list */ if (iopex -> u.hwipamlist.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); /* check caller's idea of how big an ip address is */ sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.hwipamlist.devname, iopex -> u.hwipamlist.p.devname, iopex -> u.hwipamlist.devname, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.hwipamlist.p.hwipaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.hwipamlist.p.nwipaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.hwipamlist.p.nwipmask, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_hwipamlist; break; } /* Add entry to arp cache */ case OZ_IO_IP_ARPADD: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.arpadd.p, &(iopex -> u.arpadd.p)); if (iopex -> u.arpadd.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.arpadd.p.enaddrsize > OZ_IO_ETHER_MAXADDR) return (OZ_BADPARAM); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.arpadd.devname, iopex -> u.arpadd.p.devname, iopex -> u.arpadd.devname, NULL); /* get the device name string */ if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.arpadd.p.ipaddr, iopex -> u.arpadd.ipaddr); /* get the ip address */ if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, iopex -> u.arpadd.p.enaddrsize, iopex -> u.arpadd.p.enaddr, iopex -> u.arpadd.enaddr); /* get the ethernet address */ if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_arpadd; break; } /* List hardware interface arp cache entries */ case OZ_IO_IP_ARPLIST: { movc4 (as, ap, sizeof iopex -> u.arplist.p, &(iopex -> u.arplist.p)); /* get arg list */ if (iopex -> u.arplist.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); /* check caller's idea of how big an ip address is */ if (iopex -> u.arplist.p.enaddrsize > OZ_IO_ETHER_MAXADDR) return (OZ_BADPARAM); sts = oz_knl_section_ugetz (procmode, sizeof iopex -> u.arplist.devname, iopex -> u.arplist.p.devname, iopex -> u.arplist.devname, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.arplist.p.ipaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, iopex -> u.arplist.p.enaddrsize, iopex -> u.arplist.p.enaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, sizeof *(iopex -> u.arplist.p.timeout), iopex -> u.arplist.p.timeout, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_arplist; break; } /* Remove ARP entry */ case OZ_IO_IP_ARPREM: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); return (OZ_BADIOFUNC); } /* Add routing entry */ case OZ_IO_IP_ROUTEADD: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.routeadd.p, &(iopex -> u.routeadd.p)); if (iopex -> u.routeadd.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routeadd.p.gwipaddr, iopex -> u.routeadd.gwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routeadd.p.nwipaddr, iopex -> u.routeadd.nwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routeadd.p.nwipmask, iopex -> u.routeadd.nwipmask); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_routeadd; break; } /* Remove routing entry */ case OZ_IO_IP_ROUTEREM: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.routerem.p, &(iopex -> u.routerem.p)); if (iopex -> u.routerem.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routerem.p.gwipaddr, iopex -> u.routerem.gwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routerem.p.nwipaddr, iopex -> u.routerem.nwipaddr); if (sts == OZ_SUCCESS) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.routerem.p.nwipmask, iopex -> u.routerem.nwipmask); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_routerem; break; } /* List route table entries */ case OZ_IO_IP_ROUTELIST: { movc4 (as, ap, sizeof iopex -> u.routelist.p, &(iopex -> u.routelist.p)); /* get arg list */ if (iopex -> u.routelist.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); /* check caller's idea of how big an ip address is */ sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.routelist.p.gwipaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.routelist.p.nwipaddr, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.routelist.p.nwipmask, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.routelist.p.devnamesize != 0)) sts = oz_knl_ioop_lockw (ioop, iopex -> u.routelist.p.devnamesize, iopex -> u.routelist.p.devnamebuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.routelist.p.hwipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.routelist.p.hwipaddr, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_routelist; break; } /* Add filter */ case OZ_IO_IP_FILTERADD: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.filteradd.p, &(iopex -> u.filteradd.p)); sts = oz_knl_ioop_lockr (ioop, iopex -> u.filteradd.p.size, iopex -> u.filteradd.p.data, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockr (ioop, iopex -> u.filteradd.p.size, iopex -> u.filteradd.p.mask, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_filteradd; break; } /* Remove filter */ case OZ_IO_IP_FILTERREM: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.filterrem.p, &(iopex -> u.filterrem.p)); iopex -> be = be_filterrem; break; } /* List filter */ case OZ_IO_IP_FILTERLIST: { if (procmode != OZ_PROCMODE_KNL) return (OZ_KERNELONLY); movc4 (as, ap, sizeof iopex -> u.filterlist.p, &(iopex -> u.filterlist.p)); sts = oz_knl_ioop_lockw (ioop, iopex -> u.filterlist.p.size, iopex -> u.filterlist.p.data, NULL, NULL, NULL); if (sts == OZ_SUCCESS) sts = oz_knl_ioop_lockw (ioop, iopex -> u.filterlist.p.size, iopex -> u.filterlist.p.mask, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_filterlist; break; } /* Bind socket for IP datagram reception */ case OZ_IO_IP_IPBIND: { movc4 (as, ap, sizeof iopex -> u.ipbind.p, &(iopex -> u.ipbind.p)); if (iopex -> u.ipbind.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); sts = OZ_SUCCESS; CLRIPAD (iopex -> u.ipbind.lclipaddr); /* default to any local ip address */ CLRIPAD (iopex -> u.ipbind.remipaddr); /* default to any remote ip address */ if ((sts == OZ_SUCCESS) && (iopex -> u.ipbind.p.lclipaddr != NULL)) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.ipbind.p.lclipaddr, iopex -> u.ipbind.lclipaddr); if ((sts == OZ_SUCCESS) && (iopex -> u.ipbind.p.remipaddr != NULL)) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.ipbind.p.remipaddr, iopex -> u.ipbind.remipaddr); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_ipbind; break; } /* Transmit an arbitrary IP packet */ case OZ_IO_IP_IPTRANSMIT: { movc4 (as, ap, sizeof iopex -> u.iptransmit.p, &(iopex -> u.iptransmit.p)); sts = oz_knl_ioop_lockr (ioop, iopex -> u.iptransmit.p.pktsize, iopex -> u.iptransmit.p.ippkt, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_iptransmit; break; } /* Receive an IP packet */ case OZ_IO_IP_IPRECEIVE: { movc4 (as, ap, sizeof iopex -> u.ipreceive.p, &(iopex -> u.ipreceive.p)); sts = oz_knl_ioop_lockw (ioop, iopex -> u.ipreceive.p.pktsize, iopex -> u.ipreceive.p.ippkt, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_ipreceive; break; } /* Get IP info, part 1 */ case OZ_IO_IP_IPGETINFO1: { if (!OZ_HW_WRITABLE (as, ap, procmode)) return (OZ_ACCVIO); movc4 (as, ap, sizeof iopex -> u.ipgetinfo1.p, &(iopex -> u.ipgetinfo1.p)); sts = OZ_SUCCESS; if (iopex -> u.ipgetinfo1.p.addrsize != IPADDRSIZE) sts = OZ_BADPARAM; if ((sts == OZ_SUCCESS) && (iopex -> u.ipgetinfo1.p.lclipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.ipgetinfo1.p.lclipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.ipgetinfo1.p.remipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.ipgetinfo1.p.remipaddr, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> u.ipgetinfo1.as = as; iopex -> u.ipgetinfo1.ap = ap; iopex -> be = be_ipgetinfo1; break; } /* Bind to UDP socket for incoming data */ case OZ_IO_IP_UDPBIND: { movc4 (as, ap, sizeof iopex -> u.udpbind.p, &(iopex -> u.udpbind.p)); if (iopex -> u.udpbind.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.udpbind.p.portsize != PORTSIZE) return (OZ_BADPARAM); CLRIPAD (iopex -> u.udpbind.lclipaddr); CLRPORT (iopex -> u.udpbind.lclportno); CLRIPAD (iopex -> u.udpbind.remipaddr); CLRPORT (iopex -> u.udpbind.remportno); sts = OZ_SUCCESS; if ((sts == OZ_SUCCESS) && (iopex -> u.udpbind.p.lclipaddr != NULL)) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.udpbind.p.lclipaddr, iopex -> u.udpbind.lclipaddr); if ((sts == OZ_SUCCESS) && (iopex -> u.udpbind.p.lclportno != NULL)) sts = oz_knl_section_uget (procmode, PORTSIZE, iopex -> u.udpbind.p.lclportno, iopex -> u.udpbind.lclportno); if ((sts == OZ_SUCCESS) && (iopex -> u.udpbind.p.remipaddr != NULL)) sts = oz_knl_section_uget (procmode, IPADDRSIZE, iopex -> u.udpbind.p.remipaddr, iopex -> u.udpbind.remipaddr); if ((sts == OZ_SUCCESS) && (iopex -> u.udpbind.p.remportno != NULL)) sts = oz_knl_section_uget (procmode, PORTSIZE, iopex -> u.udpbind.p.remportno, iopex -> u.udpbind.remportno); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_udpbind; break; } /* Transmit UDP packet */ case OZ_IO_IP_UDPTRANSMIT: { movc4 (as, ap, sizeof iopex -> u.udptransmit.p, &(iopex -> u.udptransmit.p)); if (iopex -> u.udptransmit.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.udptransmit.p.portsize != PORTSIZE) return (OZ_BADPARAM); sts = oz_knl_ioop_lockr (ioop, iopex -> u.udptransmit.p.rawsize, iopex -> u.udptransmit.p.rawbuff, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_udptransmit; break; } /* Receive UDP packet */ case OZ_IO_IP_UDPRECEIVE: { movc4 (as, ap, sizeof iopex -> u.udpreceive.p, &(iopex -> u.udpreceive.p)); if (iopex -> u.udpreceive.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.udpreceive.p.portsize != PORTSIZE) return (OZ_BADPARAM); sts = oz_knl_ioop_lockw (ioop, iopex -> u.udpreceive.p.rawsize, iopex -> u.udpreceive.p.rawbuff, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpreceive.p.rawrlen != NULL)) sts = oz_knl_ioop_lockw (ioop, sizeof *(iopex -> u.udpreceive.p.rawrlen), iopex -> u.udpreceive.p.rawrlen, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpreceive.p.srcipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.udpreceive.p.srcipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpreceive.p.srcportno != NULL)) sts = oz_knl_ioop_lockw (ioop, PORTSIZE, iopex -> u.udpreceive.p.srcportno, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_udpreceive; break; } /* Get UDP info, part 1 */ case OZ_IO_IP_UDPGETINFO1: { if (!OZ_HW_WRITABLE (as, ap, procmode)) return (OZ_ACCVIO); movc4 (as, ap, sizeof iopex -> u.udpgetinfo1.p, &(iopex -> u.udpgetinfo1.p)); sts = OZ_SUCCESS; if (iopex -> u.udpgetinfo1.p.addrsize != IPADDRSIZE) sts = OZ_BADPARAM; if (iopex -> u.udpgetinfo1.p.portsize != PORTSIZE) sts = OZ_BADPARAM; if ((sts == OZ_SUCCESS) && (iopex -> u.udpgetinfo1.p.lclipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.udpgetinfo1.p.lclipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpgetinfo1.p.lclportno != NULL)) sts = oz_knl_ioop_lockw (ioop, PORTSIZE, iopex -> u.udpgetinfo1.p.lclportno, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpgetinfo1.p.remipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, iopex -> u.udpgetinfo1.p.remipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (iopex -> u.udpgetinfo1.p.remportno != NULL)) sts = oz_knl_ioop_lockw (ioop, PORTSIZE, iopex -> u.udpgetinfo1.p.remportno, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> u.udpgetinfo1.as = as; iopex -> u.udpgetinfo1.ap = ap; iopex -> be = be_udpgetinfo1; break; } /* Connect to a remote TCP server */ case OZ_IO_IP_TCPCONNECT: { movc4 (as, ap, sizeof iopex -> u.tcpconnect.p, &(iopex -> u.tcpconnect.p)); if (iopex -> u.tcpconnect.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.tcpconnect.p.portsize != PORTSIZE) return (OZ_BADPARAM); iopex -> be = be_tcpconnect; break; } /* Close a TCP connection */ case OZ_IO_IP_TCPCLOSE: { movc4 (as, ap, sizeof iopex -> u.tcpclose.p, &(iopex -> u.tcpclose.p)); iopex -> be = be_tcpclose; break; } /* Listen for an inbound TCP connection */ case OZ_IO_IP_TCPLISTEN: { movc4 (as, ap, sizeof (iopex -> u.tcplisten.p), &(iopex -> u.tcplisten.p)); if (iopex -> u.tcplisten.p.addrsize != IPADDRSIZE) return (OZ_BADPARAM); if (iopex -> u.tcplisten.p.portsize != PORTSIZE) return (OZ_BADPARAM); iopex -> be = be_tcplisten; break; } /* Transmit TCP data */ case OZ_IO_IP_TCPTRANSMIT: { movc4 (as, ap, sizeof iopex -> u.tcptransmit.p, &(iopex -> u.tcptransmit.p)); sts = oz_knl_ioop_lockr (ioop, iopex -> u.tcptransmit.p.rawsize, iopex -> u.tcptransmit.p.rawbuff, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); iopex -> be = be_tcptransmit; break; } /* Receive TCP data */ case OZ_IO_IP_TCPRECEIVE: { movc4 (as, ap, sizeof iopex -> u.tcpreceive.p, &(iopex -> u.tcpreceive.p)); iopex -> be = be_tcpreceive; break; } /* Get tcp info, part 1 */ case OZ_IO_IP_TCPGETINFO1: { uByte *lclipaddr, *lclportno, *remipaddr, *remportno; if (!OZ_HW_WRITABLE (as, ap, procmode)) return (OZ_ACCVIO); movc4 (as, ap, sizeof iopex -> u.tcpgetinfo1.p, &(iopex -> u.tcpgetinfo1.p)); lclipaddr = iopex -> u.tcpgetinfo1.p.lclipaddr; lclportno = iopex -> u.tcpgetinfo1.p.lclportno; remipaddr = iopex -> u.tcpgetinfo1.p.remipaddr; remportno = iopex -> u.tcpgetinfo1.p.remportno; sts = OZ_SUCCESS; if ((iopex -> u.tcpgetinfo1.p.addrsize != IPADDRSIZE) || (iopex -> u.tcpgetinfo1.p.portsize != PORTSIZE)) sts = OZ_BADPARAM; if ((sts == OZ_SUCCESS) && (lclipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, lclipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (lclportno != NULL)) sts = oz_knl_ioop_lockw (ioop, PORTSIZE, lclportno, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (remipaddr != NULL)) sts = oz_knl_ioop_lockw (ioop, IPADDRSIZE, remipaddr, NULL, NULL, NULL); if ((sts == OZ_SUCCESS) && (remportno != NULL)) sts = oz_knl_ioop_lockw (ioop, PORTSIZE, remportno, NULL, NULL, NULL); if (sts != OZ_SUCCESS) return (sts); memclr (&(iopex -> u.tcpgetinfo1.p), sizeof iopex -> u.tcpgetinfo1.p); iopex -> u.tcpgetinfo1.p.addrsize = IPADDRSIZE; iopex -> u.tcpgetinfo1.p.portsize = PORTSIZE; iopex -> u.tcpgetinfo1.p.lclipaddr = lclipaddr; iopex -> u.tcpgetinfo1.p.lclportno = lclportno; iopex -> u.tcpgetinfo1.p.remipaddr = remipaddr; iopex -> u.tcpgetinfo1.p.remportno = remportno; iopex -> u.tcpgetinfo1.as = as; iopex -> u.tcpgetinfo1.ap = ap; iopex -> be = be_tcpgetinfo1; break; } /* Set a new TCP window */ case OZ_IO_IP_TCPWINDOW: { movc4 (as, ap, sizeof (iopex -> u.tcpwindow.p), &(iopex -> u.tcpwindow.p)); iopex -> be = be_tcpwindow; break; } /* Add DNS server to end of list */ case OZ_IO_IP_DNSSVRADD: { sts = oz_dev_ipdns_dnssvradd (procmode, as, ap, ioop); return (sts); } /* Remove DNS server from list */ case OZ_IO_IP_DNSSVRREM: { sts = oz_dev_ipdns_dnssvrrem (procmode, as, ap, ioop); return (sts); } /* List DNS servers */ case OZ_IO_IP_DNSSVRLIST: { sts = oz_dev_ipdns_dnssvrlist (procmode, as, ap, ioop); return (sts); } /* Perform DNS lookup */ case OZ_IO_IP_DNSLOOKUP: { sts = oz_dev_ipdns_dnslookup (procmode, as, ap, ioop, iopex); return (sts); } /* Who knows what */ default: { return (OZ_BADIOFUNC); } } /* Queue non-DNS request to kernel thread */ if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip ip_start: queuing iopex %p\n", iopex); iopex -> next = NULL; vl = oz_hw_smplock_wait (&smplock_vl); *iopqt = iopex; iopqt = &(iopex -> next); oz_hw_smplock_clr (&smplock_vl, vl); if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip ip_start: iopqh %p\n", iopqh); oz_knl_event_set (ipevent, 1); return (OZ_STARTED); } /************************************************************************/ /* */ /* Kernel thread that processes requests in a serial fashion */ /* */ /************************************************************************/ static uLong kthentry (void *dummy) { Abort *abort; Buf *buf; Hw *hw; int i; Iopex *iopex; Msubp *msubp; OZ_Datebin now; OZ_Thread *thread; uLong sts, vl; oz_hw_cpu_setsoftint (0); /* Initialize tables */ sts = oz_hw_tod_getnow (); if (sts == 0) sts ++; for (i = EPHEMMIN; i < EPHEMMAX; i ++) { udp_ephemsocks[i-EPHEMMIN] = 1; /* udp just uses a flag bit saying that it is available */ tcp_ephemsocks[i-EPHEMMIN] = sts; /* tcp uses a non-zero 'random' number to start their sequences out at */ } /* Start tick ast timer */ ticktimer = oz_knl_timer_alloc (); tickdatebin = oz_hw_tod_getnow (); OZ_HW_DATEBIN_ADD (tickdatebin, tickdatebin, tickdelta); oz_knl_timer_insert (ticktimer, tickdatebin, timerast, NULL); /* Queue shutdown handler to terminate tcp connections */ oz_knl_shuthand_create (shuttingdown, NULL); /* Process requests forever */ while (1) { ACCESSBUFFS_OFF; /* make sure we aren't attached to an old process */ vl = oz_hw_smplock_wait (&smplock_vl); /* lock access to lists */ /* Process abort requests */ abort = abortqh; /* see if any abort queued */ if (abort != NULL) { if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip thread: abortqh %p\n", abort); abortqh = abort -> next; /* if so, dequeue it */ if (abortqh == NULL) abortqt = &abortqh; oz_hw_smplock_clr (&smplock_vl, vl); /* release the lock */ abortproc (abort); /* process it */ continue; } /* See if there are any requests that are waiting for a transmit buffer */ /* This gets serialized through the main loop so things don't nest deeply */ /* Otherwise, theoretically, the msubp routine could get called in freebuf */ if (scanmsubps) { scanmsubps = 0; for (hw = hws; hw != NULL; hw = hw -> next) { valsndubufs (hw, __LINE__); if (((msubp = hw -> waitsndubufh) != NULL) && (hw -> freesndubufs != NULL)) { buf = mallocsndubuf (hw, NULL, NULL, msubp); /* ok, unlink buffer and msubp from lists */ (*(msubp -> entry)) (buf, msubp -> param); /* ... and call the routine to resume processing */ } valsndubufs (hw, __LINE__); } } /* Process new I/O requests */ iopex = iopqh; /* see if any I/O request queued */ if (iopex != NULL) { if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip thread: iopqh %p\n", iopex); iopqh = iopex -> next; /* if so, dequeue it */ if (iopqh == NULL) iopqt = &iopqh; oz_hw_smplock_clr (&smplock_vl, vl); /* release the lock */ ACCESSBUFFS (iopex); /* attach to corresponding process */ sts = (*(iopex -> be)) (iopex); /* start processing it */ if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip thread: iopex %p sts %u\n", iopex, sts); if (sts != OZ_STARTED) { /* maybe it finished */ ACCESSBUFFS_OFF; oz_knl_iodone (iopex -> ioop, sts, NULL, NULL, NULL); } continue; } /* Process completed receives - */ /* Since receives aren't committed to anything at this point, we immediately check for terminating hardware. Also, we assume */ /* the ethernet card won't even bother passing us bad packets, it only returns an error if the thing has completely died. */ buf = rcvqh; /* see if any receive has completed */ if (buf != NULL) { if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip thread: rcvqh %p, iosts %u\n", buf, buf -> iosts); rcvqh = buf -> next; /* if so, dequeue it */ if (rcvqh == NULL) rcvqt = &rcvqh; oz_hw_smplock_clr (&smplock_vl, vl); /* release the lock */ hw = buf -> hw; hw -> receivesinprog --; /* decrement receives-in-prog for the device */ if (hw -> terminated) { /* see if trying to terminate the device */ freebuf (buf); /* if so, free the buffer off */ hwterminate (hw); /* try to terminate for good now */ } else if (buf -> iosts != OZ_SUCCESS) { oz_knl_printk ("oz_dev_ip: error %u receiving from %s - shutting it down\n", buf -> iosts, hw -> devname); hw -> terminated = 1; /* (set this so next freebuf won't restart read) */ freebuf (buf); /* free the buffer, don't restart the read */ hwterminate (hw); /* finish shutting the interface down */ } else if (debugfailrate ()) freebuf (buf); /* maybe force it to fail (ie, ignore it) */ else if (buf -> type == BUF_TYPE_RCV_ARP) arpreadproc (buf); /* process incoming arp's */ else ipreadproc (buf); /* process incoming ip's */ continue; } /* Process completed transmits - */ /* We call the sendcomplete routine regardless if we're terminating the hardware, because the sendcomplete routine may */ /* have many interesting things to do (like post completed I/O requests). We keep the transmits-in-prog count incd so */ /* the hw struct can't go away. Then, after sendcomplete returns, we see if we can finish off the hw termination. */ buf = xmtqh; /* see if any transmit has completed */ if (buf != NULL) { if (debug & DEBUG_THREAD) PRINTF "oz_dev_ip thread: xmtqh %p\n", buf); xmtqh = buf -> next; /* if so, dequeue it */ if (xmtqh == NULL) xmtqt = &xmtqh; oz_hw_smplock_clr (&smplock_vl, vl); /* release the lock */ hw = buf -> hw; /* save pointer to hw it was transmitted on */ sendcomplete (buf); /* process it */ if ((-- (hw -> transmitsinprog) == 0) && (hw -> terminated)) hwterminate (hw); /* maybe hw can finish terminating now */ continue; } /* Process tick timer */ oz_hw_smplock_clr (&smplock_vl, vl); /* release lock */ now = oz_hw_tod_getnow (); /* see what time it is now */ if (now >= tickdatebin) { /* see if it is at or past timer's due time */ ticks ++; /* ok, increment tick counter */ OZ_HW_DATEBIN_ADD (tickdatebin, now, tickdelta); /* increment next timer due time */ timerproc (); /* process timer stuff */ oz_knl_timer_insert (ticktimer, tickdatebin, timerast, NULL); /* wake us next time timer is due */ continue; } /* If system is being shut down, terminate all tcp connections */ if (shuttingdownflag != NULL) { if (tcpcons != NULL) oz_knl_printk ("oz_dev_ip: terminating tcp connections\n"); while (tcpcons != NULL) { if (tcpcons -> reset == 0) tcpcons -> reset = OZ_SYSHUTDOWN; tcpterminate (tcpcons); } oz_knl_event_set (shuttingdownflag, 1); } /* ?? When over 25% cpu time, turn on kernel profiling so we can figure out why ?? */ #if 000 if ((ticks % TPS) == 0) { extern int oz_hw_profile_enable; OZ_Datebin thiscpu, thiswhen; static OZ_Datebin lastcpu = 0; static OZ_Datebin lastwhen; thiscpu = oz_knl_thread_gettis (oz_knl_thread_getcur (), OZ_THREAD_STATE_RUN); thiswhen = oz_hw_tod_getnow (); if (lastcpu != 0) { oz_hw_profile_enable = (((thiscpu - lastcpu) * 4) > (thiswhen - lastwhen)); } lastcpu = thiscpu; lastwhen = thiswhen; } #endif /* Nothing to do, wait for something */ oz_knl_event_waitone (ipevent); oz_knl_event_set (ipevent, 0); } } /* This routine gets called at softint level (but in who knows what thread) when the tick timer is up */ static void timerast (void *dummy, OZ_Timer *timer) { oz_knl_event_set (ipevent, 1); } /* This routine gets called at softint level when the system is being shut down */ static void shuttingdown (void *dummy) { if (oz_knl_event_create (16, "ip shutting down", NULL, &shuttingdownflag) == OZ_SUCCESS) { oz_knl_event_set (ipevent, 1); do oz_knl_event_waitone (shuttingdownflag); while (oz_knl_event_set (shuttingdownflag, 0) == 0); } } /************************************************************************/ /* */ /* Hardware interface control routines */ /* */ /************************************************************************/ /****************************/ /* Add hardware interface */ /****************************/ static uLong be_hwadd (Iopex *iopex) { Buf *buf; const char *devname; Cxb *cxb; Hw *hw; int i; OZ_IO_ether_getinfo1 ether_getinfo1; OZ_IO_ether_open ether_open; OZ_IO_ether_transmitalloc ether_transmitalloc; OZ_Iochan *iochan_arp, *iochan_ip; uByte enaddr[OZ_IO_ETHER_MAXADDR]; uLong sts; devname = iopex -> u.hwadd.devname; /* Make sure we don't already have this one defined */ for (hw = hws; hw != NULL; hw = hw -> next) { if (strcmp (devname, hw -> devname) == 0) { return (OZ_HWALRDEFINED); } } /* Assign I/O channels to ethernet device */ sts = oz_knl_iochan_crbynm (devname, OZ_LOCKMODE_CW, OZ_PROCMODE_KNL, NULL, &iochan_arp); if (sts != OZ_SUCCESS) { oz_knl_printk ("oz_dev_ip hwadd: error %u assigning channel to %s\n", sts, devname); return (sts); } sts = oz_knl_iochan_crbynm (devname, OZ_LOCKMODE_CW, OZ_PROCMODE_KNL, NULL, &iochan_ip); if (sts != OZ_SUCCESS) { oz_knl_printk ("oz_dev_ip hwadd: error %u assigning channel to %s\n", sts, devname); oz_knl_iochan_increfc (iochan_arp, -1); return (sts); } /* Open ethernet channels */ memset (ðer_open, 0, sizeof ether_open); H2NW (PROTO_ARP, ether_open.proto); sts = oz_knl_io (iochan_arp, OZ_IO_ETHER_OPEN, sizeof ether_open, ðer_open); if (sts != OZ_SUCCESS) { oz_knl_printk ("oz_dev_ip: error %u opening %s arp channel\n", sts, devname); oz_knl_iochan_increfc (iochan_arp, -1); oz_knl_iochan_increfc (iochan_ip, -1); return (sts); } H2NW (PROTO_IP, ether_open.proto); sts = oz_knl_io (iochan_ip, OZ_IO_ETHER_OPEN, sizeof ether_open, ðer_open); if (sts != OZ_SUCCESS) { oz_knl_printk ("oz_dev_ip: error %u opening %s ip channel\n", sts, devname); oz_knl_iochan_increfc (iochan_arp, -1); oz_knl_iochan_increfc (iochan_ip, -1); return (sts); } /* Get the device's ethernet address and its maximum packet size */ memset (ðer_getinfo1, 0, sizeof ether_getinfo1); ether_getinfo1.enaddrsize = OZ_IO_ETHER_MAXADDR; ether_getinfo1.enaddrbuff = enaddr; sts = oz_knl_io (iochan_arp, OZ_IO_ETHER_GETINFO1, sizeof ether_getinfo1, ðer_getinfo1); if (sts != OZ_SUCCESS) oz_crash ("oz_dev_ip: error %u getting ethernet address of %s", sts, devname); /* We only handle interfaces with a protocol size == Eproto */ if (ether_getinfo1.protosize != sizeof (Eproto)) { oz_knl_printk ("oz_dev_ip: protocol size of %s is %u, we require %u\n", devname, ether_getinfo1.protosize, sizeof (Eproto)); oz_knl_iochan_increfc (iochan_arp, -1); oz_knl_iochan_increfc (iochan_ip, -1); return (OZ_BADPARAM); } /* Allocate and fill in hardware device block */ hw = OZ_KNL_PGPMALLOQ (sizeof *hw); if (hw == NULL) { oz_knl_iochan_increfc (iochan_arp, -1); oz_knl_iochan_increfc (iochan_ip, -1); return (OZ_EXQUOTAPGP); } memset (hw, 0, sizeof *hw); hw -> devname = oz_knl_devunit_devname (oz_knl_iochan_getdevunit (iochan_ip)); hw -> iochan_arp = iochan_arp; hw -> iochan_ip = iochan_ip; hw -> ether_getinfo1 = ether_getinfo1; hw -> mtu = ether_getinfo1.datasize; if (hw -> mtu > OZ_IO_ETHER_MAXDATA) hw -> mtu = OZ_IO_ETHER_MAXDATA; /* limit sends to max that buffer can hold */ CPYENAD (hw -> myenaddr, ether_getinfo1.enaddrbuff, hw); PRINTF "oz_dev_ip: device %s, hw addr ", hw -> devname); printenaddr (hw, hw -> myenaddr); PRINTF ", enabled, mtu %u\n", hw -> mtu); /* Link it to list of available devices */ hw -> next = hws; hws = hw; /* Start receiving ARP and IP packets - receives into ethernet driver's internal buffers */ for (i = 0; i < NUM_BUFFS_RCV_ARP; i ++) { buf = OZ_KNL_PGPMALLOC (sizeof *buf); memset (buf, 0, sizeof *buf); buf -> type = BUF_TYPE_RCV_ARP; buf -> hw = hw; startread (buf); } for (i = 0; i < NUM_BUFFS_RCV_IP; i ++) { buf = OZ_KNL_PGPMALLOC (sizeof *buf); memset (buf, 0, sizeof *buf); buf -> type = BUF_TYPE_RCV_IP; buf -> hw = hw; startread (buf); } /* Set up pool of user send buffers - allocated from ethernet driver's internal buffers */ hw -> waitsndubuft = &(hw -> waitsndubufh); memset (ðer_transmitalloc, 0, sizeof ether_transmitalloc); for (i = 0; i < NUM_BUFFS_SNDU; i ++) { buf = OZ_KNL_PGPMALLOC (sizeof *buf); memset (buf, 0, sizeof *buf); ether_transmitalloc.xmtdrv_r = &(buf -> xmtdrv); ether_transmitalloc.xmteth_r = &(buf -> cxb); sts = oz_knl_io (iochan_ip, OZ_IO_ETHER_TRANSMITALLOC, sizeof ether_transmitalloc, ðer_transmitalloc); if (sts != OZ_SUCCESS) oz_crash ("oz_dev_ip: error %u allocating transmit buffers for %s", sts, hw -> devname); buf -> type = BUF_TYPE_SNDU; buf -> hw = hw; freebuf (buf); } valsndubufs (hw, __LINE__); hwchanged (); return (OZ_SUCCESS); } /***********************************************************************/ /* Remove hardware interface (and abort everything trying to use it) */ /***********************************************************************/ static uLong be_hwrem (Iopex *iopex) { Hw *hw; hw = find_hw_by_name (iopex -> u.hwrem.devname); /* look for corresponding hw struct */ if (hw == NULL) return (OZ_HWNOTDEFINED); /* if not there, error */ hwterminate (hw); return (OZ_SUCCESS); } /***************************************************/ /* Add ip address and mask to hardware interface */ /***************************************************/ static uLong be_hwipamadd (Iopex *iopex) { Arpc *arpc; Hw *hw; Hwipam *hwipam, **lhwipam; /* Find the hw that this ip address and mask are for */ hw = find_hw_by_name (iopex -> u.hwipamadd.devname); if (hw == NULL) return (OZ_HWNOTDEFINED); /* If matching nwipaddr/hwipaddr already exists for this device, remove it */ /* They might just be changing the mask */ hwipamrem (hw, iopex -> u.hwipamadd.nwipaddr, iopex -> u.hwipamadd.hwipaddr); /* Now re-add entry in order, with narrowest masks first */ for (lhwipam = &(hw -> hwipams); (hwipam = *lhwipam) != NULL;) { if (CGTIPAD (iopex -> u.hwipamadd.nwipmask, hwipam -> nwipmask)) break; } hwipam = OZ_KNL_PGPMALLOC (sizeof *hwipam); hwipam -> next = *lhwipam; CPYIPADM (hwipam -> nwipaddr, iopex -> u.hwipamadd.nwipaddr, iopex -> u.hwipamadd.nwipmask); CPYIPAD (hwipam -> nwipmask, iopex -> u.hwipamadd.nwipmask); CPYIPAD (hwipam -> hwipaddr, iopex -> u.hwipamadd.hwipaddr); *lhwipam = hwipam; /* Now put in a permanent arpc entry so that it won't try to arp for itself */ validatearpcs (hw, __LINE__); for (arpc = hw -> arpcs; arpc != NULL; arpc = arpc -> next) { /* see if there is already an arpc entry with matching ip address */ if (CEQIPAD (arpc -> ipaddr, hwipam -> hwipaddr)) break; } if (arpc == NULL) { arpc = OZ_KNL_PGPMALLOC (sizeof *arpc); /* if not, allocate an arp cache entry */ CPYIPAD (arpc -> ipaddr, hwipam -> hwipaddr); /* save the ip address */ arpc -> next = hw -> arpcs; /* link to hardware's arp cache list */ hw -> arpcs = arpc; } CPYENAD (arpc -> enaddr, hw -> myenaddr, hw); /* anyway, save the ethernet address in case the old matching ip */ /* ... address was from some other device plugged in the network */ /* ... from long ago */ arpc -> timeout = -1; /* set its timeout = forever */ hw -> arpcount ++; validatearpcs (hw, __LINE__); /* Finally, ransack stuff that references hardware in case it needs to be changed */ hwchanged (); return (OZ_SUCCESS); } /********************************************************/ /* Remove ip address and mask from hardware interface */ /********************************************************/ static uLong be_hwipamrem (Iopex *iopex) { Hw *hw; /* Find corresponding hw struct */ hw = find_hw_by_name (iopex -> u.hwipamrem.devname); if (hw == NULL) return (OZ_HWNOTDEFINED); /* Remove hwipam struct */ if (hwipamrem (hw, iopex -> u.hwipamrem.nwipaddr, iopex -> u.hwipamrem.hwipaddr)) return (OZ_SUCCESS); return (OZ_HWIPAMNOTDEF); /* Update any stuff that needs it */ hwchanged (); return (OZ_SUCCESS); } /******************************/ /* List hardware interfaces */ /******************************/ static uLong be_hwlist (Iopex *iopex) { Hw *hw; uLong sts; sts = OZ_SUCCESS; if (iopex -> u.hwlist.p.devnamebuff[0] == 0) hw = hws; else { hw = find_hw_by_name (iopex -> u.hwlist.p.devnamebuff); if (hw == NULL) sts = OZ_HWNOTDEFINED; else hw = hw -> next; } if (hw == NULL) iopex -> u.hwlist.p.devnamebuff[0] = 0; else strncpyz (iopex -> u.hwlist.p.devnamebuff, hw -> devname, iopex -> u.hwlist.p.devnamesize); return (sts); } /******************************************************/ /* List hardware interface's ip addresses and masks */ /******************************************************/ static uLong be_hwipamlist (Iopex *iopex) { Hw *hw; Hwipam *hwipam; uLong sts; sts = OZ_SUCCESS; hw = find_hw_by_name (iopex -> u.hwipamlist.devname); /* find the hardware device */ if (hw == NULL) sts = OZ_HWNOTDEFINED; /* error if not found */ else { if (ZERIPAD (iopex -> u.hwipamlist.p.hwipaddr)) hwipam = hw -> hwipams; /* if zero ip address given, return first ipam in list */ else { for (hwipam = hw -> hwipams; hwipam != NULL; hwipam = hwipam -> next) { /* otherwise, find the given ipam */ if (CEQIPAD (iopex -> u.hwipamlist.p.hwipaddr, hwipam -> hwipaddr) && CEQIPAD (iopex -> u.hwipamlist.p.nwipaddr, hwipam -> nwipaddr)) break; } if (hwipam == NULL) sts = OZ_HWIPAMNOTDEF; /* error if not found */ else hwipam = hwipam -> next; /* point to ipam entry following that one */ } if (hwipam == NULL) { CLRIPAD (iopex -> u.hwipamlist.p.hwipaddr); /* end of list, return zeroes */ CLRIPAD (iopex -> u.hwipamlist.p.nwipaddr); CLRIPAD (iopex -> u.hwipamlist.p.nwipmask); } else { CPYIPAD (iopex -> u.hwipamlist.p.hwipaddr, hwipam -> hwipaddr); /* not end, return ipam entry */ CPYIPAD (iopex -> u.hwipamlist.p.nwipaddr, hwipam -> nwipaddr); CPYIPAD (iopex -> u.hwipamlist.p.nwipmask, hwipam -> nwipmask); } } return (sts); } /*********************************/ /* Remove an Hwipam from an Hw */ /*********************************/ static int hwipamrem (Hw *hw, const uByte nwipaddr[IPADDRSIZE], const uByte hwipaddr[IPADDRSIZE]) { Arpc *arpc, **larpc; Hwipam *hwipam, **lhwipam; int found; found = 0; for (lhwipam = &(hw -> hwipams); (hwipam = *lhwipam) != NULL;) { if (!CEQIPADM (hwipam -> nwipaddr, nwipaddr, hwipam -> nwipmask) || !CEQIPAD (hwipam -> hwipaddr, hwipaddr)) { lhwipam = &(hwipam -> next); } else { *lhwipam = hwipam -> next; OZ_KNL_PGPFREE (hwipam); found = 1; } } /* Find, unlink and free (seek, locate and exterminate) any corresponding arpc struct */ /* But leave it there if there is some other hwipam with the same hwipaddr */ if (found) { for (hwipam = hw -> hwipams; hwipam != NULL; hwipam = hwipam -> next) { if (CEQIPAD (hwipam -> hwipaddr, hwipaddr)) break; } if (hwipam == NULL) { validatearpcs (hw, __LINE__); for (larpc = &(hw -> arpcs); (arpc = *larpc) != NULL; larpc = &(arpc -> next)) { if (CEQIPAD (arpc -> ipaddr, hwipaddr)) break; } if (arpc != NULL) { *larpc = arpc -> next; OZ_KNL_PGPFREE (arpc); hw -> arpcount --; validatearpcs (hw, __LINE__); } } } return (found); } /*************************************/ /* This routine nukes the given hw */ /*************************************/ static void hwterminate (Hw *hw) { Arpc *arpc; Buf *buf, *frag, **lbuf, **lfrag; Hw **lhw, *xhw; Hwipam *hwipam; Msubp *msubp; /* Let everyone know we're going away and unlink us from the hws list */ hw -> terminated = 1; for (lhw = &hws; (xhw = *lhw) != NULL; lhw = &(xhw -> next)) { if (xhw == hw) { oz_knl_printk ("oz_dev_ip: hardware %s shutting down\n", hw -> devname); *lhw = xhw -> next; break; } } /* Abort all ethernet I/O. Since hw -> terminated is set, we shouldn't get any error messages. */ oz_knl_ioabort (hw -> iochan_arp, OZ_PROCMODE_KNL); oz_knl_ioabort (hw -> iochan_ip, OZ_PROCMODE_KNL); /* Get rid of all ip addresses and masks for the hardware */ while ((hwipam = hw -> hwipams) != NULL) { hw -> hwipams = hwipam -> next; OZ_KNL_PGPFREE (hwipam); } /* Get rid of all arp cache entries for the hardware */ validatearpcs (hw, __LINE__); while ((arpc = hw -> arpcs) != NULL) { hw -> arpcs = arpc -> next; OZ_KNL_PGPFREE (arpc); } hw -> arpcount = 0; /* Get rid of all buffers waiting for arp to complete */ while ((buf = hw -> arpwaits) != NULL) { hw -> arpwaits = buf -> next; buf -> iosts = OZ_ABORTED; sendcomplete (buf); } /* Get rid of all requests waiting for a SNDU buffer */ while ((msubp = hw -> waitsndubufh) != NULL) { hw -> waitsndubufh = msubp -> next; if (hw -> waitsndubufh == NULL) hw -> waitsndubuft = &(hw -> waitsndubufh); msubp -> prev = NULL; hw -> waitsndubufc --; (*(msubp -> entry)) (NULL, msubp -> param); } /* Free off any left-over user send buffers */ while ((buf = hw -> freesndubufs) != NULL) { hw -> freesndubufs = buf -> next; hw -> freesndubufc --; freebuf (buf); } /* Free off any incoming fragments that came from this device */ for (lbuf = &frags; (buf = *lbuf) != NULL;) { for (lfrag = &(buf -> frag); (frag = *lfrag) != NULL;) { if (frag -> hw != hw) lfrag = &(frag -> frag); else { *lfrag = frag -> frag; freebuf (frag); } } if (buf -> hw != hw) lbuf = &(buf -> next); else { *lbuf = buf -> next; while ((frag = buf -> frag) != NULL) { buf -> frag = frag -> frag; freebuf (frag); } freebuf (buf); } } /* Other miscellany to be changed when hardware config changes */ hwchanged (); /* Finally, if there are no receives or transmits in progress, finish closing it out */ /* If there are still I/O's going, we will be called back when they complete */ if ((hw -> receivesinprog == 0) && (hw -> transmitsinprog == 0)) { oz_knl_iochan_increfc (hw -> iochan_arp, -1); /* now that there's no I/O going, deassigning */ oz_knl_iochan_increfc (hw -> iochan_ip, -1); /* ... should happen without any waiting */ oz_knl_printk ("oz_dev_ip: hardware %s shutdown complete\n", hw -> devname); OZ_KNL_PGPFREE (hw); } } /**********************************************************/ /* Some hardware definition was changed, fix everything */ /**********************************************************/ static void hwchanged (void) { Route *route; uByte gwipaddr[IPADDRSIZE]; /* Fix all the router definitions - they have a pointer to the best */ /* hardware to access them and what the ip address of the hardware is */ for (route = routes; route != NULL; route = route -> next) { /* scan router list */ route -> hw = find_send_hw (route -> gwipaddr, route -> hwipaddr, gwipaddr, 0); /* find the hardware to send to it */ if (route -> hw == NULL) { /* we dont do router->router->... */ PRINTF "oz_dev_ip: no direct connection to router "); /* ... to avoid routing loops */ printipaddr (route -> gwipaddr); PRINTF " serving network "); printipaddr (route -> nwipaddr); PRINTF " mask "); printipaddr (route -> nwipmask); PRINTF "\n"); route -> hw = NULL; } } } /************************************************************************/ /* */ /* ARP control routines */ /* */ /************************************************************************/ /* add an arp cache entry */ static uLong be_arpadd (Iopex *iopex) { Hw *hw; uLong timeout; hw = find_hw_by_name (iopex -> u.arpadd.p.devname); if (hw == NULL) return (OZ_HWNOTDEFINED); if (iopex -> u.arpadd.p.enaddrsize != hw -> ether_getinfo1.addrsize) return (OZ_BADPARAM); timeout = iopex -> u.arpadd.p.timeout; if (timeout == 0) timeout = ticks + ARPCACHE_LIFE; // zero = use default else if (timeout != (uLong)(-1)) timeout = timeout / MSPT + ticks; // -1: infinite, else milliseconds addtoarpc (hw, iopex -> u.arpadd.p.enaddr, iopex -> u.arpadd.p.ipaddr, timeout); return (OZ_SUCCESS); } /* list arp cache entries */ static uLong be_arplist (Iopex *iopex) { Arpc *arpc; Hw *hw; uLong sts, timeout; hw = find_hw_by_name (iopex -> u.arplist.devname); /* find the hardware device */ if (hw == NULL) sts = OZ_HWNOTDEFINED; /* error if not found */ else if (iopex -> u.arplist.p.enaddrsize != hw -> ether_getinfo1.addrsize) sts = OZ_BADPARAM; else { if (ZERIPAD (iopex -> u.arplist.p.ipaddr)) arpc = hw -> arpcs; /* if zero ip address given, return first arpc in list */ else { for (arpc = hw -> arpcs; arpc != NULL; arpc = arpc -> next) { /* otherwise, find the given arpc */ if (CEQIPAD (iopex -> u.arplist.p.ipaddr, arpc -> ipaddr)) break; } if (arpc == NULL) sts = OZ_ARPCNOTDEF; /* error if not found */ else arpc = arpc -> next; /* point to arpc entry following that one */ } *(iopex -> u.arplist.p.timeout) = 0; if (arpc == NULL) { CLRIPAD (iopex -> u.arplist.p.ipaddr); /* end of list, return zeroes */ memclr (iopex -> u.arplist.p.enaddr, hw -> ether_getinfo1.addrsize); } else { CPYIPAD (iopex -> u.arplist.p.ipaddr, arpc -> ipaddr); /* not end, return arpc entry */ CPYENAD (iopex -> u.arplist.p.enaddr, arpc -> enaddr, hw); timeout = arpc -> timeout; /* get tick timeout */ if (timeout < ticks) timeout = 0; /* maybe it's already expired */ else if (timeout != (uLong)(-1)) timeout = (timeout - ticks) * MSPT; /* else, calc milliseconds from now */ *(iopex -> u.arplist.p.timeout) = timeout; } sts = OZ_SUCCESS; } return (sts); } /************************************************************************/ /* */ /* Routing table control functions */ /* */ /************************************************************************/ /*************************/ /* Add a routing entry */ /*************************/ static uLong be_routeadd (Iopex *iopex) { int i; Route **lroute, *route, *xroute; /* Create an entry for it */ route = OZ_KNL_PGPMALLOQ (sizeof *route); if (route == NULL) return (OZ_EXQUOTAPGP); memset (route, 0, sizeof *route); CPYIPAD (route -> gwipaddr, iopex -> u.routeadd.gwipaddr); CPYIPADM (route -> nwipaddr, iopex -> u.routeadd.nwipaddr, iopex -> u.routeadd.nwipmask); CPYIPAD (route -> nwipmask, iopex -> u.routeadd.nwipmask); /* Insert in table - by descending address then descending mask */ /* This will put the narrowest masks first */ for (lroute = &routes; (xroute = *lroute) != NULL; lroute = &(xroute -> next)) { if (CGTIPAD (route -> nwipmask, xroute -> nwipmask)) break; if (CGTIPAD (route -> nwipaddr, xroute -> nwipaddr)) break; } route -> next = *lroute; *lroute = route; /* Print out message saying what was added */ oz_knl_printk ("oz_dev_ip routeadd: gateway "); printipaddr (route -> gwipaddr); oz_knl_printk (" to network "); printipaddr (route -> nwipaddr); oz_knl_printk (" mask "); printipaddr (route -> nwipmask); if (route -> next == NULL) oz_knl_printk (" (on end)\n"); else { oz_knl_printk (" (before gateway "); printipaddr (route -> next -> gwipaddr); oz_knl_printk (" to network "); printipaddr (route -> next -> nwipaddr); oz_knl_printk (" mask "); printipaddr (route -> next -> nwipmask); oz_knl_printk (")\n"); } /* Fill in the hw that the router is connected via */ hwchanged (); return (OZ_SUCCESS); } /**************************/ /* remove a routing entry */ /**************************/ static uLong be_routerem (Iopex *iopex) { Route **lroute, *xroute; /* Find first matching entry */ for (lroute = &routes; (xroute = *lroute) != NULL; lroute = &(xroute -> next)) { if (CEQIPAD (xroute -> gwipaddr, iopex -> u.routerem.gwipaddr) && CEQIPAD (xroute -> nwipaddr, iopex -> u.routerem.nwipaddr) && CEQIPAD (xroute -> nwipmask, iopex -> u.routerem.nwipmask)) break; } if (xroute == NULL) return (OZ_NOSUCHROUTE); /* Remove it from list */ xroute -> next = *lroute; *lroute = xroute; OZ_KNL_PGPFREE (xroute); return (OZ_SUCCESS); } /************************/ /* list routing entries */ /************************/ static uLong be_routelist (Iopex *iopex) { Route *route; uLong sts; sts = OZ_SUCCESS; if (ZERIPAD (iopex -> u.routelist.p.gwipaddr)) route = routes; /* if zero ip address given, return first route in list */ else { for (route = routes; route != NULL; route = route -> next) { /* otherwise, find the given entry */ if (CEQIPAD (iopex -> u.routelist.p.gwipaddr, route -> gwipaddr) && CEQIPAD (iopex -> u.routelist.p.nwipaddr, route -> nwipaddr) && CEQIPAD (iopex -> u.routelist.p.nwipmask, route -> nwipmask)) break; } if (route == NULL) sts = OZ_NOSUCHROUTE; /* error if not found */ else route = route -> next; /* point to route entry following that one */ } if (iopex -> u.routelist.p.devnamesize != 0) iopex -> u.routelist.p.devnamebuff[0] = 0; if (iopex -> u.routelist.p.hwipaddr != NULL) CLRIPAD (iopex -> u.routelist.p.hwipaddr); if (route == NULL) { CLRIPAD (iopex -> u.routelist.p.gwipaddr); /* end of list, return zeroes */ CLRIPAD (iopex -> u.routelist.p.nwipaddr); CLRIPAD (iopex -> u.routelist.p.nwipmask); } else { CPYIPAD (iopex -> u.routelist.p.gwipaddr, route -> gwipaddr); /* not end, return route entry */ CPYIPAD (iopex -> u.routelist.p.nwipaddr, route -> nwipaddr); CPYIPAD (iopex -> u.routelist.p.nwipmask, route -> nwipmask); if (route -> hw != NULL) { if (iopex -> u.routelist.p.devnamesize != 0) strncpyz (iopex -> u.routelist.p.devnamebuff, route -> hw -> devname, iopex -> u.routelist.p.devnamesize); if (iopex -> u.routelist.p.hwipaddr != NULL) CPYIPAD (iopex -> u.routelist.p.hwipaddr, route -> hwipaddr); } } return (sts); } /************************************************************************/ /* */ /* Filter table control functions */ /* */ /************************************************************************/ /************************/ /* Add a filter entry */ /************************/ static uLong be_filteradd (Iopex *iopex) { Filter *filter, **lfilter; int index, acount, astart; /* Decode the filter list to add to */ lfilter = NULL; if (iopex -> u.filteradd.p.listid == OZ_IO_IP_FILTER_INPUT) lfilter = &input_filters; if (iopex -> u.filteradd.p.listid == OZ_IO_IP_FILTER_FORWARD) lfilter = &forward_filters; if (iopex -> u.filteradd.p.listid == OZ_IO_IP_FILTER_OUTPUT) lfilter = &output_filters; if (lfilter == NULL) return (OZ_BADPARAM); /* Skip over the indexed ones already in the list */ index = iopex -> u.filteradd.p.index; while (-- index >= 0) { filter = *lfilter; if (filter == NULL) return (OZ_SUBSCRIPT); lfilter = &(filter -> next); } /* Trim off any trailing zero bytes. Mask bytes of zeroes are always a match so why bother with them. */ while (iopex -> u.filteradd.p.size > 0) { // repeat while there are bytes at the end to check if (((uByte *)(iopex -> u.filteradd.p.mask))[iopex->u.filteradd.p.size-1] != 0) break; // stop when we hit a non-zero byte -- (iopex -> u.filteradd.p.size); // zero byte at end, reduce size to chop it off } /* Skip leading longwords of zeroes in the element being added */ astart = 0; // assume we won't skip any while (iopex -> u.filteradd.p.size >= 4) { // repeat while there are whole longwords to check if (*(uLong *)(iopex -> u.filteradd.p.mask) != 0) break; // stop if we have a non-zero mask longword iopex -> u.filteradd.p.size -= 4; // zero, reduce the size by a longword ((uLong *)(iopex -> u.filteradd.p.data)) ++; // increment pointers to next longword ((uLong *)(iopex -> u.filteradd.p.mask)) ++; astart ++; // remember we skipped over a longword at the beginning } /* Allocate the filter block with enough 'a' elements to cover the supplied mask and data */ acount = (iopex -> u.filteradd.p.size + 3) / 4; // this is how many elements we need in 'a' array if (acount == 0) acount = 1; // filter_check reqvires a non-zero array element count filter = OZ_KNL_PGPMALLOQ (sizeof *filter + acount * sizeof (filter -> a[0])); // allocate memory if (filter == NULL) return (OZ_EXQUOTAPGP); // if too big, return error /* Fill in filter header */ filter -> next = *lfilter; // what will be next in chain filter -> action = iopex -> u.filteradd.p.action; // what action to take when filter matches filter -> acount = acount; // how many longwords to compare filter -> astart = astart; // what longword to start comparing at /* Copy whole longwords from supplied data to internal buffer */ /* Keep the bytes in network order to make filter_check fast */ acount = 0; // start counting array elements while (iopex -> u.filteradd.p.size >= 4) { // repeat as long as there are whole longwords filter -> a[acount].data = *(uLong *)(iopex -> u.filteradd.p.data); // copy data longword filter -> a[acount].mask = *(uLong *)(iopex -> u.filteradd.p.mask); // copy mask longword iopex -> u.filteradd.p.size -= 4; // reduce size by a longword (uByte *)(iopex -> u.filteradd.p.data) += 4; // increment pointers by a longword (uByte *)(iopex -> u.filteradd.p.mask) += 4; acount ++; // increment array index } /* Copy any part of left over bytes, padded to a longword */ if (iopex -> u.filteradd.p.size > 0) { // see if any left over bytes filter -> a[acount].data = 0; // if so, pad them with zeroes filter -> a[acount].mask = 0; memcpy (&(filter -> a[acount].data), iopex -> u.filteradd.p.data, iopex -> u.filteradd.p.size); // this should work memcpy (&(filter -> a[acount].mask), iopex -> u.filteradd.p.mask, iopex -> u.filteradd.p.size); // ... endian-independent acount ++; // we padded to a whole longword, so count it } /* Filter_check reqvires a non-zero array element count */ if (acount == 0) { // check for zero filter -> a[0].data = 0; // fill in an entry of zeroes filter -> a[0].mask = 0; acount = 1; // now there is one element } filter -> acount = acount; // save final longword count /* Link it into filter list */ #if 000 oz_knl_printk ("oz_dev_ip filteradd: %d[%d]\n", iopex -> u.filteradd.p.listid, iopex -> u.filteradd.p.index); oz_knl_dumpmem (sizeof *filter + (acount - 1) * sizeof (filter -> a[0]), filter); #endif *lfilter = filter; return (OZ_SUCCESS); } /*************************/ /* Remove filter entry */ /*************************/ static uLong be_filterrem (Iopex *iopex) { Filter *filter, **lfilter; lfilter = NULL; if (iopex -> u.filterrem.p.listid == OZ_IO_IP_FILTER_INPUT) lfilter = &input_filters; if (iopex -> u.filterrem.p.listid == OZ_IO_IP_FILTER_FORWARD) lfilter = &forward_filters; if (iopex -> u.filterrem.p.listid == OZ_IO_IP_FILTER_OUTPUT) lfilter = &output_filters; if (lfilter == NULL) return (OZ_BADPARAM); while (-- (iopex -> u.filterrem.p.index) >= 0) { filter = *lfilter; if (filter == NULL) return (OZ_SUBSCRIPT); lfilter = &(filter -> next); } filter = *lfilter; if (filter == NULL) return (OZ_SUBSCRIPT); *lfilter = filter -> next; OZ_KNL_PGPFREE (filter); return (OZ_SUCCESS); } /***********************/ /* List filter entry */ /***********************/ static uLong be_filterlist (Iopex *iopex) { Filter *filter, **lfilter; int acount; uLong ldata, lmask, sts; lfilter = NULL; if (iopex -> u.filterlist.p.listid == OZ_IO_IP_FILTER_INPUT) lfilter = &input_filters; if (iopex -> u.filterlist.p.listid == OZ_IO_IP_FILTER_FORWARD) lfilter = &forward_filters; if (iopex -> u.filterlist.p.listid == OZ_IO_IP_FILTER_OUTPUT) lfilter = &output_filters; if (lfilter == NULL) return (OZ_BADPARAM); for (filter = *lfilter; filter != NULL; filter = filter -> next) { if (-- (iopex -> u.filterlist.p.index) < 0) goto foundit; } return (OZ_SUBSCRIPT); foundit: /* Return the action code if requested */ if (iopex -> u.filterlist.p.action_r != NULL) { sts = oz_knl_section_uput (iopex -> procmode, sizeof *(iopex -> u.filterlist.p.action_r), &(filter -> action), iopex -> u.filterlist.p.action_r); if (sts != OZ_SUCCESS) return (sts); } /* If buffer can't even hold the leading zeroes, zero the buffers and return overflow status */ if (iopex -> u.filterlist.p.size < filter -> astart * 4) { memset (iopex -> u.filterlist.p.data, 0, iopex -> u.filterlist.p.size); memset (iopex -> u.filterlist.p.mask, 0, iopex -> u.filterlist.p.size); return (OZ_BUFFEROVF); } /* Ok, put in leading zeroes */ memset (iopex -> u.filterlist.p.data, 0, filter -> astart * 4); memset (iopex -> u.filterlist.p.mask, 0, filter -> astart * 4); iopex -> u.filterlist.p.size -= filter -> astart * 4; ((uLong *)(iopex -> u.filterlist.p.data)) += filter -> astart; ((uLong *)(iopex -> u.filterlist.p.mask)) += filter -> astart; /* Return any whole longwords the caller can take and that we have */ acount = 0; while ((iopex -> u.filterlist.p.size >= 4) && (acount < filter -> acount)) { *(uLong *)(iopex -> u.filterlist.p.data) = filter -> a[acount].data; *(uLong *)(iopex -> u.filterlist.p.mask) = filter -> a[acount].mask; iopex -> u.filterlist.p.size -= 4; ((uLong *)(iopex -> u.filterlist.p.data)) ++; ((uLong *)(iopex -> u.filterlist.p.mask)) ++; acount ++; } /* If we gave all we got, fill caller's buffers with zeroes */ if (acount == filter -> acount) { memset (iopex -> u.filterlist.p.data, 0, iopex -> u.filterlist.p.size); memset (iopex -> u.filterlist.p.mask, 0, iopex -> u.filterlist.p.size); } /* Otherwise, try to return the last few bytes to caller then zero fill */ else { memcpy (iopex -> u.filterlist.p.data, &(filter -> a[acount].data), iopex -> u.filterlist.p.size); memcpy (iopex -> u.filterlist.p.mask, &(filter -> a[acount].mask), iopex -> u.filterlist.p.size); if (++ acount < filter -> acount) return (OZ_BUFFEROVF); ldata = filter -> a[acount].data; lmask = filter -> a[acount].mask; memset (&ldata, 0, iopex -> u.filterlist.p.size); memset (&lmask, 0, iopex -> u.filterlist.p.size); if ((ldata | lmask) != 0) return (OZ_BUFFEROVF); } return (OZ_SUCCESS); } /************************************************************************/ /* */ /* Raw IP packet I/O routines */ /* */ /************************************************************************/ static uLong be_ipbind (Iopex *iopex) { Chnex *chnex; chnex = iopex -> chnex; if (chnex -> ipbind_iopqt != NULL) return (OZ_CHANALRBOUND); /* Set up the chnex struct to reference specific ip packet conditions */ CPYIPAD (chnex -> ipbind_lclipaddr, iopex -> u.ipbind.lclipaddr); /* get local ip address */ CPYIPAD (chnex -> ipbind_remipaddr, iopex -> u.ipbind.remipaddr); /* get remote ip address */ chnex -> ipbind_proto = iopex -> u.ipbind.p.proto; /* get ip protocol */ chnex -> ipbind_passiton = iopex -> u.ipbind.p.passiton; /* see whether or not to pass it on for normal processing */ chnex -> ipbind_next = ipbinds; /* enable it */ chnex -> ipbind_iopqh = NULL; chnex -> ipbind_iopqt = &(chnex -> ipbind_iopqh); chnex -> ip_threadid = THREADID (iopex); ipbinds = chnex; return (OZ_SUCCESS); } /*************************************/ /* Start transmitting an ip packet */ /*************************************/ static uLong be_iptransmit (Iopex *iopex) { Buf *buf; Hw *hw; /* Make sure it's not going to Vermont (you can't get there from here) */ hw = find_send_hw (iopex -> u.iptransmit.p.ippkt -> dstipaddr, NULL, NULL, 1); if (hw == NULL) return (OZ_NOROUTETODEST); /* Make sure the message fits in a single IP packet */ if (iopex -> u.iptransmit.p.pktsize > hw -> mtu) return (OZ_BUFFEROVF); /* Ok, queue send buffer malloc request */ iopex -> chnex -> ip_transcount ++; iopex -> chnex -> ip_threadid = THREADID (iopex); buf = mallocsndubuf (hw, iptransmit_mem, iopex, &(iopex -> msubp)); if (buf != NULL) iptransmit_mem (buf, iopex); return (OZ_STARTED); } /* There is a buffer available for the request, start sending the packet */ static void iptransmit_mem (Buf *buf, void *iopexv) { Iopex *iopex; uLong sts; iopex = iopexv; sts = OZ_NOROUTETODEST; if (buf != NULL) { ACCESSBUFFS (iopex); memcpy (IPPKT (buf -> hw, buf -> cxb), iopex -> u.iptransmit.p.ippkt, iopex -> u.iptransmit.p.pktsize); sts = sendip (buf, iptransmit_done, iopex); } if (sts != OZ_STARTED) { ACCESSBUFFS_OFF; oz_knl_iodone (iopex -> ioop, sts, NULL, NULL, NULL); } } static void iptransmit_done (void *iopexv, uLong status) { ACCESSBUFFS_OFF; oz_knl_iodone (((Iopex *)iopexv) -> ioop, status, NULL, NULL, NULL); } /**********************************/ /* Start receiving an ip packet */ /**********************************/ static uLong be_ipreceive (Iopex *iopex) { Chnex *chnex; chnex = iopex -> chnex; if (chnex -> ipbind_iopqt == NULL) return (OZ_CHANOTBOUND); /* it must have been bound with OZ_IO_IP_IPBIND */ *(chnex -> ipbind_iopqt) = iopex; /* put request on end of queue */ chnex -> ipbind_iopqt = &(iopex -> next); chnex -> ip_recvcount ++; chnex -> ip_threadid = THREADID (iopex); iopex -> next = NULL; return (OZ_STARTED); } /**************************/ /* Get ip info, part 1 */ /**************************/ static uLong be_ipgetinfo1 (Iopex *iopex) { Chnex *chnex; Iopex *riopex; chnex = iopex -> chnex; /* If ipbind_iopqt is NULL, it is not bound, so return zeroes */ if (chnex -> ipbind_iopqt == NULL) { if (iopex -> u.ipgetinfo1.p.lclipaddr != NULL) CLRIPAD (iopex -> u.ipgetinfo1.p.lclipaddr); if (iopex -> u.ipgetinfo1.p.remipaddr != NULL) CLRIPAD (iopex -> u.ipgetinfo1.p.remipaddr); iopex -> u.ipgetinfo1.p.recvpend = -1; } /* It is bound, return what it is bound to */ else { if (iopex -> u.ipgetinfo1.p.lclipaddr != NULL) CPYIPAD (iopex -> u.ipgetinfo1.p.lclipaddr, chnex -> ipbind_lclipaddr); if (iopex -> u.ipgetinfo1.p.remipaddr != NULL) CPYIPAD (iopex -> u.ipgetinfo1.p.remipaddr, chnex -> ipbind_remipaddr); iopex -> u.ipgetinfo1.p.recvpend = 0; for (riopex = chnex -> ipbind_iopqh; riopex != NULL; riopex = riopex -> next) { iopex -> u.ipgetinfo1.p.recvpend ++; } } iopex -> u.ipgetinfo1.p.proto = chnex -> ipbind_proto; iopex -> u.ipgetinfo1.p.passiton = chnex -> ipbind_passiton; iopex -> u.ipgetinfo1.p.recvcount = chnex -> ip_recvcount; iopex -> u.ipgetinfo1.p.transcount = chnex -> ip_transcount; iopex -> u.ipgetinfo1.p.threadid = chnex -> ip_threadid; movc4 (sizeof iopex -> u.ipgetinfo1.p, &(iopex -> u.ipgetinfo1.p), iopex -> u.ipgetinfo1.as, iopex -> u.ipgetinfo1.ap); return (OZ_SUCCESS); } /************************************************************************/ /* */ /* UDP I/O routines */ /* */ /************************************************************************/ static uLong be_udpbind (Iopex *iopex) { Chnex *chnex; int ephem; chnex = iopex -> chnex; /* If udpbind_iopqt is not NULL, it is already bound to a udp socket */ if (chnex -> udpbind_iopqt != NULL) return (OZ_CHANALRBOUND); /* Not bound already, set channel up on the udpbinds list */ chnex -> udpbind_ephem = 0; CPYIPAD (chnex -> udpbind_lclipaddr, iopex -> u.udpbind.lclipaddr); CPYPORT (chnex -> udpbind_lclportno, iopex -> u.udpbind.lclportno); CPYIPAD (chnex -> udpbind_remipaddr, iopex -> u.udpbind.remipaddr); CPYPORT (chnex -> udpbind_remportno, iopex -> u.udpbind.remportno); if (ZERPORT (chnex -> udpbind_lclportno)) { for (ephem = udp_ephemnext; udp_ephemsocks[ephem-EPHEMMIN] == 0;) { ephem ++; if (ephem == EPHEMMAX) ephem = EPHEMMIN; if (ephem == udp_ephemnext) return (OZ_NOMOREPHEMPORTS); } udp_ephemnext = ephem + 1; if (udp_ephemnext == EPHEMMAX) udp_ephemnext = EPHEMMIN; udp_ephemsocks[ephem-EPHEMMIN] = 0; H2NW (ephem, chnex -> udpbind_lclportno); chnex -> udpbind_ephem = ephem; } chnex -> udpbind_iopqh = NULL; chnex -> udpbind_iopqt = &(chnex -> udpbind_iopqh); chnex -> udpbind_next = udpbinds; chnex -> udp_threadid = THREADID (iopex); udpbinds = chnex; return (OZ_SUCCESS); } /*************************************/ /* Start transmitting udp datagram */ /*************************************/ static uLong be_udptransmit (Iopex *iopex) { Buf *buf; Hw *hw; Ippkt *ippkt; /* Make sure it's not going to Vermont (you can't get there from here) */ hw = find_send_hw (iopex -> u.udptransmit.p.dstipaddr, iopex -> u.udptransmit.myipaddr, NULL, 1); if (hw == NULL) return (OZ_NOROUTETODEST); /* Make up an ip packet identification */ while (++ ident == 0) {} iopex -> u.udptransmit.ident = ident; /* Increment channel's udp transmit count */ iopex -> chnex -> udp_transcount ++; iopex -> chnex -> udp_threadid = THREADID (iopex); /* Process 'no fragmentation required' separately here */ if (ippkt -> dat.udp.raw - (uByte *)ippkt + iopex -> u.udptransmit.p.rawsize <= hw -> mtu) { buf = mallocsndubuf (hw, udptransmit_mem, iopex, &(iopex -> msubp)); if (buf != NULL) udptransmit_mem (buf, iopex); return (OZ_STARTED); } /* Fragmentation required */ return (OZ_BUFFEROVF); } /* There is a buffer available, start transmitting datagram */ static void udptransmit_mem (Buf *buf, void *iopexv) { Iopex *iopex; Ippkt *ippkt; uLong sts; uWord cksm, len; iopex = iopexv; sts = OZ_NOROUTETODEST; if (buf != NULL) { ippkt = IPPKT (buf -> hw, buf -> cxb); /* point to ip packet */ ippkt -> hdrlenver = 0x45; /* header length and version */ ippkt -> typeofserv = 0; /* type of service */ len = ippkt -> dat.udp.raw + iopex -> u.udptransmit.p.rawsize - (uByte *)ippkt; H2NW (len, ippkt -> totalen); /* total ip packet length (includes ip header, udp header and data) */ H2NW (iopex -> u.udptransmit.ident, ippkt -> ident); /* identification (used for re-assembly) */ H2NW (0, ippkt -> flags); /* no flags required */ ippkt -> ttl = 255; /* time-to-live */ ippkt -> proto = PROTO_IP_UDP; /* protocol */ CPYIPAD (ippkt -> srcipaddr, iopex -> u.udptransmit.myipaddr); /* source ip address = local machine */ CPYPORT (ippkt -> dat.udp.sport, iopex -> chnex -> udpbind_lclportno); /* source port = port that we're bound to (if any) */ CPYIPAD (ippkt -> dstipaddr, iopex -> u.udptransmit.p.dstipaddr); /* destination ip address = as given */ CPYPORT (ippkt -> dat.udp.dport, iopex -> u.udptransmit.p.dstportno); /* destination udp port = as given */ len = ippkt -> dat.udp.raw + iopex -> u.udptransmit.p.rawsize - (uByte *)&(ippkt -> dat.udp); H2NW (len, ippkt -> dat.udp.length); /* udp length (includes udp header and length of data) */ ACCESSBUFFS (iopex); /* attach to requestor's process to access buffer */ memcpy (ippkt -> dat.udp.raw, iopex -> u.udptransmit.p.rawbuff, iopex -> u.udptransmit.p.rawsize); /* copy in the data */ H2NW (0, ippkt -> dat.udp.cksm); /* set up new checksum */ cksm = oz_dev_ip_udpcksm (ippkt); H2NW (cksm, ippkt -> dat.udp.cksm); sts = sendip (buf, udptransmit_done, iopex); /* start sending it, post request when done */ } if (sts != OZ_STARTED) { ACCESSBUFFS_OFF; oz_knl_iodone (iopex -> ioop, sts, NULL, NULL, NULL); } } static void udptransmit_done (void *iopexv, uLong status) { ACCESSBUFFS_OFF; oz_knl_iodone (((Iopex *)iopexv) -> ioop, status, NULL, NULL, NULL); } /***********************************/ /* Start receiving an udp packet */ /***********************************/ static uLong be_udpreceive (Iopex *iopex) { Chnex *chnex; chnex = iopex -> chnex; /* Make sure it is bound to a socket or we'll never get anything */ if (chnex -> udpbind_iopqt == NULL) return (OZ_CHANOTBOUND); /* Put iopex on end of channel's read request queue */ *(chnex -> udpbind_iopqt) = iopex; chnex -> udpbind_iopqt = &(iopex -> next); iopex -> next = NULL; chnex -> udp_recvcount ++; chnex -> udp_threadid = THREADID (iopex); return (OZ_STARTED); } /**************************/ /* Get udp info, part 1 */ /**************************/ static uLong be_udpgetinfo1 (Iopex *iopex) { Chnex *chnex; Iopex *riopex; chnex = iopex -> chnex; /* If udpbind_iopqt is NULL, it is not bound to an udp socket, so return zeroes */ if (chnex -> udpbind_iopqt == NULL) { if (iopex -> u.udpgetinfo1.p.lclipaddr != NULL) CLRIPAD (iopex -> u.udpgetinfo1.p.lclipaddr); if (iopex -> u.udpgetinfo1.p.lclportno != NULL) CLRPORT (iopex -> u.udpgetinfo1.p.lclportno); if (iopex -> u.udpgetinfo1.p.remipaddr != NULL) CLRIPAD (iopex -> u.udpgetinfo1.p.remipaddr); if (iopex -> u.udpgetinfo1.p.remportno != NULL) CLRPORT (iopex -> u.udpgetinfo1.p.remportno); iopex -> u.udpgetinfo1.p.recvpend = -1; } /* It is bound, return what it is bound to */ else { if (iopex -> u.udpgetinfo1.p.lclipaddr != NULL) CPYIPAD (iopex -> u.udpgetinfo1.p.lclipaddr, chnex -> udpbind_lclipaddr); if (iopex -> u.udpgetinfo1.p.lclportno != NULL) CPYPORT (iopex -> u.udpgetinfo1.p.lclportno, chnex -> udpbind_lclportno); if (iopex -> u.udpgetinfo1.p.remipaddr != NULL) CPYIPAD (iopex -> u.udpgetinfo1.p.remipaddr, chnex -> udpbind_remipaddr); if (iopex -> u.udpgetinfo1.p.remportno != NULL) CPYPORT (iopex -> u.udpgetinfo1.p.remportno, chnex -> udpbind_remportno); iopex -> u.udpgetinfo1.p.recvpend = 0; for (riopex = chnex -> udpbind_iopqh; riopex != NULL; riopex = riopex -> next) { iopex -> u.udpgetinfo1.p.recvpend ++; } } iopex -> u.udpgetinfo1.p.recvcount = chnex -> udp_recvcount; iopex -> u.udpgetinfo1.p.transcount = chnex -> udp_transcount; iopex -> u.udpgetinfo1.p.threadid = chnex -> udp_threadid; movc4 (sizeof iopex -> u.udpgetinfo1.p, &(iopex -> u.udpgetinfo1.p), iopex -> u.udpgetinfo1.as, iopex -> u.udpgetinfo1.ap); return (OZ_SUCCESS); } /************************************************************************/ /* */ /* TCP connection oriented I/O routines */ /* */ /************************************************************************/ static uLong be_genpoll (Iopex *iopex) { Long transmitsize; Tcpcon *tcpcon; uLong alreadydone; /* Make sure we're connected or connecting to something */ tcpcon = iopex -> chnex -> tcpcon; if (tcpcon == NULL) return (OZ_CHANOTBOUND); /* Make sure connection is not closed or aborted */ if (tcpcon -> tcpclosed) return (OZ_LINKCLOSED); if (tcpcon -> reset != 0) return (tcpcon -> reset); /* See if it's already satisfied */ alreadydone = 0; if (iopex -> u.genpoll.p.mask & OZ_IO_GEN_POLL_R) { if ((tcpcon -> rcvwindownxt != tcpcon -> rcvwindowins) || (tcpcon -> rcvdfin) || (tcpcon -> reset != 0)) { alreadydone |= OZ_IO_GEN_POLL_R; } } if (iopex -> u.genpoll.p.mask & OZ_IO_GEN_POLL_W) { transmitsize = tcpcon -> seq_lastrcvdwsize - tcpcon -> seq_nextusersendata; if (transmitsize > 0) alreadydone |= OZ_IO_GEN_POLL_W; } if (alreadydone != 0) return (OZ_POLLDONE + alreadydone); /* If not, queue it */ *(tcpcon -> poll_iopqt) = iopex; iopex -> next = NULL; tcpcon -> poll_iopqt = &(iopex -> next); return (OZ_STARTED); } /************************************************************************/ /* Start connecting to a server */ /************************************************************************/ static uLong be_tcpconnect (Iopex *iopex) { Buf *buf; Chnex *chnex; Hw *hw; Tcpcon *tcpcon; Sock lsockno, rsockno; uByte myipaddr[IPADDRSIZE]; uLong seq, sts; uWord maxsendsize; chnex = iopex -> chnex; /* Can't already have a connection on this channel */ if (chnex -> tcpcon != NULL) return (OZ_CHANALRBOUND); /* Find out if we have a way to get to the server and get my ip address associated with it */ hw = find_send_hw (iopex -> u.tcpconnect.p.dstipaddr, myipaddr, NULL, 1); if (hw == NULL) return (OZ_NOROUTETODEST); /* Allocate an unused ephemeral socket number if caller did not supply one */ lsockno = 0; if (iopex -> u.tcpconnect.p.srcportno != NULL) lsockno = N2HW (iopex -> u.tcpconnect.p.srcportno); rsockno = N2HW (iopex -> u.tcpconnect.p.dstportno); if (lsockno == 0) { lsockno = alloctcpephem (&seq); if (lsockno == 0) return (OZ_NOMOREPHEMPORTS); } /* An explicit socket number was given, make sure it is not in use */ else { for (tcpcon = tcpcons; tcpcon != NULL; tcpcon = tcpcon -> next) { if ((tcpcon -> lsockno == lsockno) && (tcpcon -> rsockno == rsockno) && CEQIPAD (tcpcon -> ripaddr, iopex -> u.tcpconnect.p.dstipaddr)) { return (OZ_SOCKNOINUSE); } } seq = oz_hw_tod_getnow (); } /* Fill in new connection block */ sts = calloctcpcon (iopex, iopex -> u.tcpconnect.p.windowsize, iopex -> u.tcpconnect.p.windowbuff, &tcpcon); if (sts != OZ_SUCCESS) return (sts); tcpcon -> window99s = iopex -> u.tcpconnect.p.window99s; tcpcon -> next = tcpcons; /* next in tcpcons list */ tcpcon -> rcvchnex = chnex; /* save pointer to receiving channel */ tcpcon -> seq_nextusersendata = seq; /* this is sequence of first byte we will send out */ tcpcon -> seq_lastrcvdack = seq; /* pretend the remote has acked all we set up to this point */ tcpcon -> nextxmtseq = seq; /* next thing to transmit */ tcpcon -> seq_lastrcvdwsize = seq + MINWINSIZE; /* pretend the remote has sent us a minimal wsize */ /* ... so we will at least be able to send the SYN */ tcpcon -> lastxmtreq = &(tcpcon -> nextackreq); CPYIPAD (tcpcon -> lipaddr, myipaddr); /* set up client's ip address based on what interface we are using */ CPYIPAD (tcpcon -> ripaddr, iopex -> u.tcpconnect.p.dstipaddr); /* save server host's ip address */ tcpcon -> lsockno = lsockno; /* save local (ephemeral) port number */ tcpcon -> rsockno = rsockno; /* save remote server's port number */ tcpcon -> maxsendsize = TCP_MAXSENDSIZE (hw); /* maximum size to send in a datagram */ tcpcon -> congestionwindow = TCP_MAXSENDSIZE (hw); tcpcon -> slowstarthresh = 65535; tcpcon -> retransmitinterval = TPS; /* initial retransmit interval = one second */ tcpcon -> smoothedroundtrip = TPS * 8; /* corresponding smoothed round trip time (ticks * 8) */ tcpcon -> timeout = ticks + iopex -> u.tcpconnect.p.timeout / MSPT; if (iopex -> u.tcpconnect.p.timeout == 0) tcpcon -> timeout = -1; tcpcon -> tcprcv_iopqt = &(tcpcon -> tcprcv_iopqh); /* initialize receive request queue */ tcpcon -> poll_iopqt = &(tcpcon -> poll_iopqh); /* initialize poll request queue */ tcpcons = tcpcon; /* link it on list */ ntcpcons ++; chnex -> tcpcon = tcpcon; /* remeber connection is in progress on the channel */ if (debug & DEBUG_ERRORS) { PRINTF "new outbound "); printtcpcon (tcpcon); PRINTF "\n"); } validtcpxmtq (tcpcon); chnex -> tcp_threadid = THREADID (iopex); /* Start sending connection request to server. If/when server acknowledges it, */ /* request will be complete. If it times out, the connection will be aborted. */ if (debug & DEBUG_IOPOST) PRINTF "oz_dev_ip*: starting tcpconnect iopex %p, tcpcon %p\n", iopex, tcpcon); memset (&(iopex -> u.tcptransmit), 0, sizeof iopex -> u.tcptransmit); /* make up a transmit packet */ iopex -> u.tcptransmit.p.rawsize = 1; /* SYN messages consume 1 sequence number */ iopex -> u.tcptransmit.syn = TCP_FLAGS_SYN | (1 << TCP_FLAGS_HLNSHF); /* it is a SYN message */ queuetcptransmit (iopex, tcpcon); /* start sending it */ return (OZ_STARTED); } /************************************************************************/ /* Close the connection */ /************************************************************************/ static uLong be_tcpclose (Iopex *iopex) { uLong sts; Tcpcon *tcpcon; tcpcon = iopex -> chnex -> tcpcon; /* point to associated connection block */ if (tcpcon == NULL) return (OZ_CHANOTBOUND); /* If abort style, just shut the connection down right now, wipe it all out */ if (iopex -> u.tcpclose.p.abort) { if (tcpcon -> reset == 0) tcpcon -> reset = OZ_ABORTED; tcpterminate (tcpcon); return (OZ_SUCCESS); } /* If normal style, queue a transmit buffer that has rawsize=1,rawbuff=NULL. The transmit */ /* routine (starttcpsend) will translate this into a FIN packet to be transmitted normally. */ memset (&(iopex -> u.tcptransmit), 0, sizeof iopex -> u.tcptransmit); /* otherwise, make up a transmit packet */ iopex -> u.tcptransmit.p.rawsize = 1; /* FIN messages consume 1 sequence number */ iopex -> u.tcptransmit.syn = TCP_FLAGS_FIN | TCP_FLAGS_ACK; /* it is a FIN message */ tcpcon -> tcpclosed = 1; /* remember we closed the connection */ queuetcptransmit (iopex, tcpcon); /* send it, transmit routine will make a FIN packet */ return (OZ_STARTED); } /************************************************************************/ /* Start listening for an inbound connection */ /************************************************************************/ static uLong be_tcplisten (Iopex *iopex) { Chnex *chnex; Tcpcon *tcpcon; uLong seq, sts; /* We can't already be either trying to connect to a server or listening for an inbound connection on this channel */ chnex = iopex -> chnex; if (chnex -> tcpcon != NULL) return (OZ_CHANALRBOUND); /* Ok, start listening for an inbound connection */ sts = calloctcpcon (iopex, iopex -> u.tcplisten.p.windowsize, iopex -> u.tcplisten.p.windowbuff, &tcpcon); if (sts != OZ_SUCCESS) return (sts); tcpcon -> lsockno = N2HW (iopex -> u.tcplisten.p.lclportno); /* save local port number that clients will connect to (required) */ if (tcpcon -> lsockno == 0) { tcpcon -> lsockno = alloctcpephem (&seq); /* none given, get an ephemeral socket number */ if (tcpcon -> lsockno == 0) return (OZ_NOMOREPHEMPORTS); } else seq = oz_hw_tod_getnow (); if (iopex -> u.tcplisten.p.lclipaddr != NULL) { CPYIPAD (tcpcon -> lipaddr, iopex -> u.tcplisten.p.lclipaddr); /* this is the ip address that they have to be connecting to (in case this computer */ } /* has more than one ip address and the caller only wants stuff for one of them) */ if (iopex -> u.tcplisten.p.remportno != NULL) { tcpcon -> rsockno = N2HW (iopex -> u.tcplisten.p.remportno); /* save remote port number (in case caller only wants connections from a specific remote port number) */ } if (iopex -> u.tcplisten.p.remipaddr != NULL) { CPYIPAD (tcpcon -> ripaddr, iopex -> u.tcplisten.p.remipaddr); /* save remote ip address (in case caller only wants connections from a specific remote ip address) */ } tcpcon -> window99s = iopex -> u.tcplisten.p.window99s; tcpcon -> lisiopex = iopex; /* we use this iopex to send the SYN/ACK message */ tcpcon -> tcprcv_iopqt = &(tcpcon -> tcprcv_iopqh); /* initialize receive request queue */ tcpcon -> poll_iopqt = &(tcpcon -> poll_iopqh); /* initialize poll request queue */ tcpcon -> lastxmtreq = &(tcpcon -> nextackreq); /* initialize transmit request queue */ tcpcon -> rcvchnex = chnex; /* i/o channel that is doing the connect/listen */ tcpcon -> timeout = -1; /* listens do not time out */ tcpcon -> seq_nextusersendata = seq; /* this is sequence of first byte we will send out */ tcpcon -> seq_lastrcvdack = seq; /* pretend the remote has acked all we set up to this point */ tcpcon -> nextxmtseq = seq; /* next thing to transmit */ tcpcon -> seq_lastrcvdwsize = seq; /* wait for the remote to give us a window size when it connects */ iopex -> u.tcplisten.tcpcon = tcpcon; iopex -> next = NULL; /* link it to master list so when an inbound connection */ iopex -> u.tcplisten.prev = tcplisqt; /* ... packet arrives, we will see this listen request */ chnex -> tcpcon = tcpcon; /* remember we're doing a listen on this channel */ *tcplisqt = iopex; tcplisqt = &(iopex -> next); chnex -> tcp_threadid = THREADID (iopex); return (OZ_STARTED); } /************************************************************************/ /* Start transmitting data to the remote end */ /* */ /* This implementation does not post the request's completion until */ /* it has received the corresponding ack from the remote end. This */ /* eliminates a memcpy as these routines do not have to keep a copy */ /* of the data for retransmission. So to get good thruput for large */ /* amounts of data, the application should double-buffer transmits. */ /* */ /* Note that you can queue transmits before the connection or listen */ /* request has completed, and the transmits will start as soon as the */ /* request completes. */ /************************************************************************/ static uLong be_tcptransmit (Iopex *iopex) { Buf *buf; Tcpcon *tcpcon; /* Make sure we're connected or connecting to something */ tcpcon = iopex -> chnex -> tcpcon; if (tcpcon == NULL) return (OZ_CHANOTBOUND); /* Make sure connection is not closed or aborted */ if (tcpcon -> tcpclosed) return (OZ_LINKCLOSED); if (tcpcon -> reset != 0) return (tcpcon -> reset); /* Queue it for transmission */ iopex -> chnex -> tcp_transcount ++; iopex -> chnex -> tcp_threadid = THREADID (iopex); iopex -> u.tcptransmit.syn = 0; queuetcptransmit (iopex, tcpcon); /* It will complete when all the data has been acked */ return (OZ_STARTED); } /************************************************************************/ /* The user is starting a tcp receive request. We put the request on */ /* the end of the queue then