static u08 ip_receive_packet( ip_header * p, u16 len ) { // log_printf( "ip: got ip packet, proto=%d\n", p->proto ); if (!__ip_validate_header( p )) { log_printf( "ip: bad ip checksum\n" ); return 1; } switch( p->proto ) { case IPPROTO_ICMP: return icmp_receive_packet( p, len ); case IPPROTO_UDP: return udp_receive_packet( p, len ); case IPPROTO_TCP: return tcp_receive_packet( p, len ); } return 1; // did we eat it? yes we did... }
int32 udp_receive (DEVICE *dptr, int32 link, uint16 *pdata, uint16 maxbuf) { // Receive an IMP packet from the virtual modem. pdata is a pointer usually // directly into H316 simulated memory) to where the IMP packet data should // be stored, and maxbuf is the maximum length of that buffer in H316 words // (not bytes!). If a message is successfully received then this routine // returns the length, again in H316 words, of the IMP packet. The caller // can detect buffer overflows by comparing this result to maxbuf. If no // packets are waiting right now then zero is returned, and -1 is returned // in the event of any fatal socket I/O error. // // This routine also handles checking for unsolicited messages and duplicate // or out of sequence messages. All of these are unceremoniously discarded. // // One final note - it's explicitly allowed for pdata to be null and/or // maxbuf to be zero. In either case the received package is discarded, but // the actual length of the discarded package is still returned. UDP_PACKET pkt; int32 pktlen, explen, implen, i; uint32 magic, pktseq; if ((link < 0) || (link >= MAXLINKS)) return SCPE_IERR; if (!udp_links[link].used) return SCPE_IERR; while ((pktlen = udp_receive_packet(link, &pkt)) > 0) { // First do some header checks for a valid UDP packet ... if (pktlen < UDP_HEADER_LEN) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/o header (length=%d)\n", link, pktlen); continue; } magic = ntohl(pkt.magic); if (magic != MAGIC) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/bad magic number (magic=%08x)\n", link, magic); continue; } implen = ntohs(pkt.count); explen = UDP_HEADER_LEN + implen*sizeof(uint16); if (explen != pktlen) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet length wrong (expected=%d received=%d)\n", link, explen, pktlen); continue; } // Now the hard part = check the sequence number. The rxsequence value is // the number of the next packet we expect to receive - that's the number // this packet should have. If this packet's sequence is less than that, // then this packet is out of order or a duplicate and we discard it. If // this packet is greater than that, then we must have missed one or two // packets. In that case we MUST update rxsequence to match this one; // otherwise the two ends could never resynchronize after a lost packet. pktseq = ntohl(pkt.sequence); if (pktseq < udp_links[link].rxsequence) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet out of sequence 1 (expected=%d received=%d\n", link, udp_links[link].rxsequence, pktseq); continue; } if (pktseq != udp_links[link].rxsequence) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet out of sequence 2 (expected=%d received=%d\n", link, udp_links[link].rxsequence, pktseq); } udp_links[link].rxsequence = pktseq+1; // It's a valid packet - if there's no buffer then just discard it. if ((pdata == NULL) || (maxbuf == 0)) { sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet discarded (no buffer available)\n", link); return implen; } // Copy the data to the H316 memory and we're done! sim_debug(IMP_DBG_UDP, dptr, "link %d - packet received (sequence=%d, length=%d)\n", link, pktseq, pktlen); for (i = 0; i < (implen < maxbuf ? implen : maxbuf); ++i) *pdata++ = ntohs(pkt.data[i]); return implen; } // Here if pktlen <= 0 ... return pktlen; }
int udp_receive (int link, uint8_t * pdata, uint16_t maxbuf) { // Receive an packet from the virtual modem. pdata is a pointer to where // the IMP packet data should be stored, and maxbuf is the maximum length // of that buffer in bytes. If a message is // successfully received then this routine returns the length, again in // bytes, of the packet. The caller can detect buffer overflows by // comparing this result to maxbuf. If no packets are waiting right now // then zero is returned, and -1 is returned in the event of any fatal // socket I/O error. // // This routine also handles checking for unsolicited messages and // duplicate or out of sequence messages. All of these are // unceremoniously discarded. // // One final note - it's explicitly allowed for pdata to be null and/or // maxbuf to be zero. In either case the received package is discarded, but // the actual length of the discarded package is still returned. UDP_PACKET pkt; int32_t pktlen, explen, implen, i; uint32_t magic, pktseq; if ((link < 0) || (link >= MAXLINKS)) return -1; if (!udp_links [link] . used) return -1; //if (dptr != udp_links [link] . dptr) //return SCPE_IERR; while ((pktlen = udp_receive_packet (link, & pkt, sizeof (pkt))) > 0) { // First do some header checks for a valid UDP packet ... if (((size_t) pktlen) < UDP_HEADER_LEN) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/o header (length=%d)\n", link, pktlen); continue; } magic = pkt . magic; if (magic != MAGIC) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/bad magic number (magic=%08x)\n", link, magic); continue; } implen = pkt . count; explen = UDP_HEADER_LEN + implen * sizeof (uint8_t); if (explen != pktlen) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet length wrong (expected=%d received=%d)\n", link, explen, pktlen); continue; } // Now the hard part = check the sequence number. The rxsequence value // is the number of the next packet we expect to receive - that's the // number this packet should have. If this packet's sequence is less // than that, then this packet is out of order or a duplicate and we // discard it. If this packet is greater than that, then we must have // missed one or two packets. In that case we MUST update rxsequence // to match this one; otherwise the two ends could never resynchronize // after a lost packet./ And there's one final complication to worry // about - if the simh on the other end is restarted for some reason, // then his sequence numbers will reset to zero. In that case we'll // never recover synchronization without special efforts. The hack is // to check for a packet sequence number of zero and, if we find it, // force synchronization. This improves the situation, but I freely // admit that it's possible to think of a number of cases where this // also fails. The only absolute solution is to implement a more // complicated system with non-IMP control messages exchanged between // the modem emulation on both ends. That'd be nice, but I'll leave it // as an exercise for later. pktseq = pkt . sequence; if ((pktseq == 0) && (udp_links [link] . rxsequence != 0)) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - remote modem restarted\n", link); } else if (pktseq < udp_links [link] . rxsequence) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet out of sequence 1 (expected=%d received=%d\n", link, udp_links[link].rxsequence, pktseq); continue; // discard this packet! } else if (pktseq != udp_links [link] . rxsequence) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet out of sequence 2 (expected=%d received=%d\n", link, udp_links[link].rxsequence, pktseq); } udp_links [link] . rxsequence = pktseq + 1; // It's a valid packet - if there's no buffer then just discard it. if ((pdata == NULL) || (maxbuf == 0)) { //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet discarded (no buffer available)\n", link); return implen; } // Copy the data and we're done! //sim_debug (IMP_DBG_UDP, dptr, "link %d - packet received (sequence=%d, length=%d)\n", link, pktseq, pktlen); printf ("link %d - packet received (sequence=%d, length=%d)\n", link, pktseq, pktlen); for (i = 0; i < (implen < maxbuf ? implen : maxbuf); ++ i) * pdata ++ = pkt . data [i]; return implen; } // Here if pktlen <= 0 ... return pktlen; }