# /* internet.c */ /* EMACS_MODES: c !fill */ /* Internet protocol layer for the MIT network system. The following * routines are included: * in_open Open an internet connection * in_write Write an internet packet to the net * in_read Read an internet packet from the net * in_close Close an internet connection * in_find_data Return ptr. to data portion of internet packet * in_header Return ptr to internet header of in packet * in_alloc Allocate buffer space for an internet packet * in_free Free an internet packet buffer * inn_copy Copy an internet name * in_logpkt Log a packet to the standard output * * Note that use of this package requires the use of the standard I/O * library. */ #include #include #include "params.h" #include "ip.h" #include "netdefs.h" #include /* The connection array is an array of connection structures, one per * file descriptor. It is filled in at connection open time by in_open, * and is used by in_write to obtain the default values for internet * header fields. */ static struct conn { unshort cn_prot; /* protocol for this connection */ struct in_name cn_fhost; /* foreign host */ unshort cn_maxfrag; /* max. fragment size */ } conn[NOFILE]; static unshort lnhsiz; /* size of local net header */ static unshort lntsiz; /* size of local net trailer */ long ip_dropped; /* number of dropped pkts */ long ip_sent; /* number of packets sent */ long ip_rcvd; /* number of packets rcvd */ static struct in_name in_addr0 = { /* an all-zero internet address */ 0, 0, 0, 0, }; extern int errno; /* last system error */ in_open (prot, fhost, fsock, lsock) /* Open an internet connection on the specified protocol, to the * specified foreign host, with the specified local and foreign * sockets. Note that any or all of the arguments may be NULL * to indicate that field is to be ignored in packet demultiplexing. * Set up the connection array entry for this connection. Also * get the local net header/trailer sizes for this connection * and the maximum packet size for fragmentation purposes. * Return the file descriptor, or -1 for error (with the error * specified in the global variable errno). * * Arguments: */ unshort prot; /* InterNet protocol ID */ reg struct in_name *fhost; /* foreign host addr */ unshort fsock; /* foreign socket */ unshort lsock; /* local socket */ { struct netdf ndf; /* net connection definition */ reg int fd; /* file descriptor */ reg struct conn *cp; /* conn. ptr. for this fd */ struct { int hsize; int tsize; } htarg; /* arg for NIOCGETHT ioctl */ ndf.nd_prot1 = MITINET; /* set up connection definition */ ndf.nd_prot2 = prot; inn_copy ((fhost != NULL) ? fhost : &in_addr0, &ndf.nd_fhost); ndf.nd_fsock = fsock; ndf.nd_lsock = lsock; if ((fd = netopen (NETDEV, &ndf)) < 0) /* try to open */ return (fd); /* failed */ cp = &conn[fd]; /* set up conn. table entry */ cp->cn_prot = prot; inn_copy (&ndf.nd_fhost, &cp->cn_fhost); ioctl(fd, NIOCGETMAXPKT, &cp->cn_maxfrag); /* get max frag size */ ioctl(fd, NIOCGETHT, &htarg); /* get header and tailer sizes */ lnhsiz = htarg.hsize; lntsiz = htarg.tsize; ioctl(fd, NIOCGETHOST, &myhost); /* get my host address */ return (fd); } in_write (fd, buf, datalen, prot, fhost, fill) /* Write the specified packet to the net. The buf argument is a * pointer to the start of a packet buffer, such as one allocated * by in_alloc. The packet buffer is assumed to have space at * the beginning for local net and internet headers. It is also * assumed that there is enough space in the packet buffer to * pad the packet up to an even number of bytes, because all * transmitted packets must be an even number of bytes in * length. (If the packet was obtained by in_alloc or by * in_read this will be the case). * This routine fills in the internet header, using the data stored * in the connection table array for the specified file descriptor * and the data from the arguments. If a non-NULL prot argument * is supplied, it is used; otherwise the prot argument from the * connection table is used. The foreign host is handled similarly. * The fill argument, if FALSE, specifies that the header fields * have already been filled in (probably by a previous call to * in_write) and hence do not need to be touched; this permits * quick retransmission of packets. * Returns the number of bytes written if successful; if not, * returns -1 and the system error code in global errno. * * Arguments: */ int fd; /* file descriptor for this conn */ caddr_t buf; /* pointer to start of pkt */ reg unshort datalen; /* length of data portion of pkt */ unshort prot; /* if non-NULL, protocol to use */ struct in_name *fhost; /* if non-NULL, foreign host */ int fill; /* if TRUE, fill internet header */ { reg struct ip *pip; /* ptr. to internet hdr. of pkt */ unshort len; /* total packet length */ unshort stat; /* write status */ pip = (struct ip *)(buf + lnhsiz); /* point to IN hdr */ if (fill) { /* need to fill header? */ pip->ip_ihl = IP_IHL; /* yep, do it */ pip->ip_ver = IP_VER; pip->ip_tsrv = IP_TSRV; pip->ip_len = datalen + IPHSIZ; pip->ip_foff = 0; /* %%TBC%% no fragmentation yet */ pip->ip_flgs = 0; /* %%TBC%% no fragmentation yet */ pip->ip_time = IP_TIME; pip->ip_prot = (prot != 0 ? prot : conn[fd].cn_prot); inn_copy ((fhost != NULL) ? fhost : &conn[fd].cn_fhost, &pip->ip_dest); #ifndef BIGINDIAN ipswab (pip); /* if needed, swap integer fields */ #endif } pip->ip_id = 0; /* regardless, get new inet id */ pip->ip_chksum = CHKSUM; inn_copy (&in_addr0, &pip->ip_src); len = lnhsiz + datalen + IPHSIZ + lntsiz; len = (len + 1) & ~1; /* round up to even no. of bytes */ do { /* write it out */ stat = write (fd, buf, len); } while (stat < len && errno == EINTR); ip_sent++; return (stat); } caddr_t in_read (fd, plen) /* Read the next available internet packet for the specified connection from * the net. First, allocate a big enough buffer, then read the packet in. * Return a pointer to the start of the received packet. Also, in *plen * return the total length of the received packet in bytes. * The routine in_find_data may be called to obtain a pointer to the start * of the data area of the packet. * If no packet is available (read returns NULL) return NULL. * Note: bad packets are simply dropped, and the dropped * packet count is incremented. * * Arguments: */ int fd; /* file descriptor for this conn. */ int *plen; /* place to return length of pkt */ { reg int rcvlen; /* length of actual received pkt */ reg int maxlen; /* max. inet packet length */ reg caddr_t pkt; /* ptr. to start of actual pkt */ maxlen = lnhsiz + IPHSIZ + INETLEN + lntsiz; if ((pkt = calloc (1, maxlen)) == NULL) return (NULL); if ((rcvlen = read (fd, pkt, maxlen)) <= (lnhsiz + IPHSIZ)) { cfree (pkt); ip_dropped++; return (NULL); } ip_rcvd++; *plen = rcvlen; return (pkt); } caddr_t in_find_data (ppkt, plen) /* Return a pointer to the data area of the packet pointed to by * ppkt. The specified packet is assumed to either have just * been received from the net, or to have just been allocated * by in_alloc; in either case, the data area of the packet is * preceeded by a local net header and an internet header. * Also return the length of the data area of the packet in bytes * in the integer pointed to by plen. */ reg caddr_t ppkt; /* ptr. to packet */ int *plen; /* place to put length of data */ { reg struct ip *pip; /* ptr. to internet header */ reg caddr_t pdata; /* ptr. to data area */ pip = (struct ip *)(ppkt + lnhsiz); pdata = ppkt + lnhsiz + (pip->ip_ihl << 2); *plen = pip->ip_len - (pip->ip_ihl << 2); return (pdata); } struct ip *in_header (ppkt) /* Return a pointer to the internet header portion of the specified * packet. NOTA BENE: most programs should never need to look at the * internet header, and hence this routine should not be used in * general. It is included for protocols like ICMP which DO need * to see the internet header. * * Arguments: */ reg caddr_t ppkt; /* ptr. to packet */ { return ((struct ip *)(ppkt + lnhsiz)); } in_close (fd) /* Close the network connection specified by fd. Simply call down * to the system close routine for now. */ int fd; /* network file descriptor */ { close (fd); } caddr_t in_alloc (datalen) /* Allocate the storage for a packet with data area of size datalen, * including space for local net header and trailer and for internet * header. The length will be increased to an even number of bytes * if needed. Set up the internet header length fields so in_find_data * will work properly. Return a pointer to the allocated packet, * or NULL if unable to allocate one. * * Arguments: */ reg int datalen; /* length of data area in bytes */ { caddr_t pkt; /* ptr. to allocated packet */ reg struct ip *pip; /* ptr. to internet header */ reg int len; /* total packet length */ len = lnhsiz + IPHSIZ + datalen + lntsiz; len = (len + 1) & ~1; /* round up to even byte size */ if ((pkt = calloc (1, len)) == NULL) return (NULL); pip = (struct ip *)(pkt + lnhsiz); pip->ip_ihl = IP_IHL; pip->ip_len = datalen + (IP_IHL << 2); return (pkt); } in_free (pkt) /* Free the packet pointed to by pkt. Just call down to the C free * routine. * * Arguments: */ caddr_t pkt; /* ptr. to pkt to free */ { cfree (pkt); } #ifndef BIGINDIAN ipswab( ppkt ) /* * Swap bytes in integer fields (except sumcheck fields) * of internet packet. */ struct ip *ppkt; /* ptr. to internet hdr. of pkt */ { static int swabds[] = { &0->ip_len, &0->ip_id, (&0->ip_id) + 1, }; swaba( (char *)ppkt, swabds, (sizeof(swabds)/sizeof(swabds[0]))); return; } #endif inn_copy (p1, p2) /* Copy the internet name pointed to by p1 to the space pointed to * by p2. */ reg struct in_name *p1; reg struct in_name *p2; { p2->in_net = p1->in_net; p2->in_hhost = p1->in_hhost; p2->in_mhost = p1->in_mhost; p2->in_lhost = p1->in_lhost; } #define min(x, y) ((x) < (y) ? (x) : (y)) in_logpkt (ppkt, datalen, dir) /* Log the specified packet out to the standard output. * * Arguments: */ reg char *ppkt; /* ptr. to packet */ int datalen; /* length of data area */ int dir; /* packet direction: */ /* INPKT - input */ /* OUTPKT - output */ { reg int len; /* total packet length */ reg int i; /* bytes per line counter */ int linelen; /* temp for bytes per line */ if (dir == INPKT) printf ("Input packet:\n"); else printf ("Output packet:\n"); len = lnhsiz + IPHSIZ + datalen + lntsiz; while (len > 0) { linelen = min(16, len); for (i = 0; i < linelen; i++, len--) printf ("%3o ", (*ppkt++ & 0377)); printf ("\n"); } }