/* Check remote password */ static int chkrpass( struct mbuf *bp) { char *lbuf; uint16 len; int rval = 0; len = len_p(bp); if(Rempass == 0 || *Rempass == 0 || strlen(Rempass) != len) return rval; lbuf = (char *) mallocw(len); pullup(&bp,lbuf,len); if(strncmp(Rempass,lbuf,len) == 0) rval = 1; free(lbuf); return rval; }
/* Dump a UDP header */ void udp_dump( FILE *fp, struct mbuf **bpp, int32 source,int32 dest, int check) /* If 0, bypass checksum verify */ { struct udp udp; struct pseudo_header ph; uint csum; if(bpp == NULL || *bpp == NULL) return; fprintf(fp,"UDP:"); /* Compute checksum */ ph.source = source; ph.dest = dest; ph.protocol = UDP_PTCL; ph.length = len_p(*bpp); if((csum = cksum(&ph,*bpp,ph.length)) == 0) check = 0; /* No checksum error */ ntohudp(&udp,bpp); fprintf(fp," len %u",udp.length); fprintf(fp," %u->%u",udp.source,udp.dest); if(udp.length > UDPHDR) fprintf(fp," Data %u",udp.length - UDPHDR); if(udp.checksum == 0) check = 0; if(check) fprintf(fp," CHECKSUM ERROR (%u)",csum); putc('\n',fp); switch(udp.dest){ case IPPORT_RIP: rip_dump(fp,bpp); } }
/* Encode a packet in SLIP format */ static struct mbuf * slip_encode(struct mbuf **bpp) { struct mbuf *lbp; /* Mbuf containing line-ready packet */ register uint8 *cp; int c; /* Allocate output mbuf that's twice as long as the packet. * This is a worst-case guess (consider a packet full of FR_ENDs!) */ lbp = alloc_mbuf(2*len_p(*bpp) + 2); if(lbp == NULL){ /* No space; drop */ free_p(bpp); return NULL; } cp = lbp->data; /* Flush out any line garbage */ *cp++ = FR_END; /* Copy input to output, escaping special characters */ while((c = PULLCHAR(bpp)) != -1){ switch(c){ case FR_ESC: *cp++ = FR_ESC; *cp++ = T_FR_ESC; break; case FR_END: *cp++ = FR_ESC; *cp++ = T_FR_END; break; default: *cp++ = c; } } *cp++ = FR_END; lbp->cnt = cp - lbp->data; return lbp; }
/* Dump packet bytes, no interpretation */ void raw_dump( struct iface *ifp, int direction, struct mbuf *bp) { struct mbuf *tbp; FILE *fp; if((fp = ifp->trfp) == NULL) return; fprintf(fp,"\n******* raw packet dump (%s)\n", ((direction & IF_TRACE_OUT) ? "send" : "recv")); dup_p(&tbp,bp,0,len_p(bp)); if(tbp != NULL) hex_dump(fp,&tbp); else fprintf(fp,nospace); fprintf(fp,"*******\n"); free_p(&tbp); }
/* Execute random quench algorithm on an interface's output queue */ void rquench( struct iface *ifp, int drop ){ struct mbuf *bp,*bplast; int i; struct qhdr qhdr; struct ip ip; struct mbuf *bpdup; if((i = len_q(ifp->outq)) == 0) return; /* Queue is empty */ i = urandom(i); /* Select a victim */ /* Search for i-th message on queue */ bplast = NULL; for(bp = ifp->outq;bp != NULL && i>0;i--,bplast=bp,bp=bp->anext) ; if(bp == NULL) return; /* "Can't happen" */ /* Send a source quench */ dup_p(&bpdup,bp,0,len_p(bp)); pullup(&bpdup,&qhdr,sizeof(qhdr)); ntohip(&ip,&bpdup); icmp_output(&ip,bpdup,ICMP_QUENCH,0,NULL); free_p(&bpdup); if(!drop) return; /* All done */ /* Drop the packet */ if(bplast != NULL) bplast->anext = bp->anext; else ifp->outq = bp->anext; /* First on list */ free_p(&bp); }
/* Process remote command */ static void uremote( struct iface *iface, struct udp_cb *up, int cnt) { struct mbuf *bp; struct socket fsock; char command; int32 addr; recv_udp(up,&fsock,&bp); command = PULLCHAR(&bp); switch(command & 0xff) { case SYS__EXIT: if(chkrpass(bp) == 0) { logmsg(NULL,"%s - Remote exit PASSWORD FAIL", pinet_udp(&fsock)); } else { logmsg(NULL,"%s - Remote exit PASSWORD OK", pinet_udp(&fsock)); main_exit = 1; } break; case KICK__ME: if(len_p(bp) >= sizeof(int32)) addr = pull32(&bp); else addr = fsock.address; kick(addr); /*** smtptick((void *)addr); ***/ break; } free_p(&bp); }
/* Send an IP datagram. Modeled after the example interface on p 32 of * RFC 791 */ int ip_send( int32 source, /* source address */ int32 dest, /* Destination address */ char protocol, /* Protocol */ char tos, /* Type of service */ char ttl, /* Time-to-live */ struct mbuf **bpp, /* Data portion of datagram */ uint16 length, /* Optional length of data portion */ uint16 id, /* Optional identification */ char df /* Don't-fragment flag */ ){ struct ip ip; /* IP header */ ipOutRequests++; if(bpp == NULL) return -1; if(source == INADDR_ANY) source = locaddr(dest); if(length == 0 && *bpp != NULL) length = len_p(*bpp); if(id == 0) id = Id_cntr++; if(ttl == 0) ttl = ipDefaultTTL; /* Fill in IP header */ ip.version = IPVERSION; ip.tos = tos; ip.length = IPLEN + length; ip.id = id; ip.offset = 0; ip.flags.mf = 0; ip.flags.df = df; ip.flags.congest = 0; ip.ttl = ttl; ip.protocol = protocol; ip.source = source; ip.dest = dest; ip.optlen = 0; if(Ip_trace) dumpip(NULL,&ip,*bpp,0); htonip(&ip,bpp,IP_CS_NEW); if(ismyaddr(ip.dest)){ /* Pretend it has been sent by the loopback interface before * it appears in the receive queue */ #ifdef SIM net_sim(bpp); #else net_route(&Loopback,bpp); #endif Loopback.ipsndcnt++; Loopback.rawsndcnt++; Loopback.lastsent = secclock(); } else net_route(NULL,bpp); return 0; }
/* Reassemble incoming IP fragments and dispatch completed datagrams * to the proper transport module */ void ip_recv( struct iface *iface, /* Incoming interface */ struct ip *ip, /* Extracted IP header */ struct mbuf **bpp, /* Data portion */ int rxbroadcast, /* True if received on subnet broadcast address */ int32 spi /* Security association, if any */ ){ /* Function to call with completed datagram */ register struct raw_ip *rp; struct mbuf *bp1; int rxcnt = 0; register struct iplink *ipp; /* If we have a complete packet, call the next layer * to handle the result. Note that fraghandle passes back * a length field that does NOT include the IP header */ if(bpp == NULL || fraghandle(ip,bpp) == -1) return; /* Not done yet */ /* Trim data segment if necessary. */ trim_mbuf(bpp,ip->length - (IPLEN + ip->optlen)); ipInDelivers++; if(Ip_trace) dumpip(iface,ip,*bpp,spi); for(rp = Raw_ip;rp != NULL;rp = rp->next){ if(rp->protocol != ip->protocol) continue; rxcnt++; /* Duplicate the data portion, and put the header back on */ dup_p(&bp1,*bpp,0,len_p(*bpp)); if(bp1 != NULL){ htonip(ip,&bp1,IP_CS_OLD); enqueue(&rp->rcvq,&bp1); if(rp->r_upcall != NULL) (*rp->r_upcall)(rp); } else { free_p(&bp1); } } /* Look it up in the transport protocol table */ for(ipp = Iplink;ipp->funct != NULL;ipp++){ if(ipp->proto == ip->protocol) break; } if(ipp->funct != NULL){ /* Found, call transport protocol */ (*ipp->funct)(iface,ip,bpp,rxbroadcast,spi); } else { /* Not found */ if(rxcnt == 0){ /* Send an ICMP Protocol Unknown response... */ ipInUnknownProtos++; /* ...unless it's a broadcast */ if(!rxbroadcast){ icmp_output(ip,*bpp,ICMP_DEST_UNREACH, ICMP_PROT_UNREACH,NULL); } } free_p(bpp); } }
void dump( struct iface *ifp, int direction, struct mbuf *bp ){ struct mbuf *tbp; uint size; time_t timer; char *cp; struct iftype *ift; FILE *fp; if(ifp == NULL || (ifp->trace & direction) == 0 || (fp = ifp->trfp) == NULL) return; /* Nothing to trace */ ift = ifp->iftype; switch(direction){ case IF_TRACE_IN: if((ifp->trace & IF_TRACE_NOBC) && ift != NULL && (ift->addrtest != NULL) && (*ift->addrtest)(ifp,bp) == 0) return; /* broadcasts are suppressed */ timer = (time_t) secclock(); cp = ctime(&timer); cp[24] = '\0'; fprintf(fp,"\n%s - %s recv:\n",cp,ifp->name); break; case IF_TRACE_OUT: timer = (time_t) secclock(); cp = ctime(&timer); cp[24] = '\0'; fprintf(fp,"\n%s - %s sent:\n",cp,ifp->name); break; } if(bp == NULL || (size = len_p(bp)) == 0){ fprintf(fp,"empty packet!!\n"); return; } dup_p(&tbp,bp,0,size); if(tbp == NULL){ fprintf(fp,nospace); return; } if(ift != NULL && ift->trace != NULL) (*ift->trace)(fp,&tbp,1); if(ifp->trace & IF_TRACE_ASCII){ /* Dump only data portion of packet in ascii */ ascii_dump(fp,&tbp); } else if(ifp->trace & IF_TRACE_HEX){ /* Dump entire packet in hex/ascii */ free_p(&tbp); dup_p(&tbp,bp,0,len_p(bp)); if(tbp != NULL) hex_dump(fp,&tbp); else fprintf(fp,nospace); } free_p(&tbp); }
/* Return an ICMP response to the sender of a datagram. * Unlike most routines, the callER frees the mbuf. */ int icmp_output( struct ip *ip, /* Header of offending datagram */ struct mbuf *data, /* Data portion of datagram - FREED BY CALLER */ uint8 type, /* Codes to send */ uint8 code, union icmp_args *args ){ struct mbuf *bp; struct icmp icmp; /* ICMP protocol header */ uint dlen; /* Length of data portion of offending pkt */ uint length; /* Total length of reply */ if(ip == NULL) return -1; if(ip->protocol == ICMP_PTCL){ /* Peek at type field of ICMP header to see if it's safe to * return an ICMP message */ switch(data->data[0]){ case ICMP_ECHO_REPLY: case ICMP_ECHO: case ICMP_TIMESTAMP: case ICMP_TIME_REPLY: case ICMP_INFO_RQST: case ICMP_INFO_REPLY: break; /* These are all safe */ default: /* Never send an ICMP error message about another * ICMP error message! */ return -1; } } /* Compute amount of original datagram to return. * We return the original IP header, and up to 8 bytes past that. */ dlen = min(8,len_p(data)); length = dlen + ICMPLEN + IPLEN + ip->optlen; /* Take excerpt from data portion */ if(data != NULL && dup_p(&bp,data,0,dlen) == 0) return -1; /* The caller will free data */ /* Recreate and tack on offending IP header */ htonip(ip,&bp,IP_CS_NEW); icmp.type = type; icmp.code = code; icmp.args.unused = 0; switch(icmp.type){ case ICMP_PARAM_PROB: icmpOutParmProbs++; icmp.args.pointer = args->pointer; break; case ICMP_REDIRECT: icmpOutRedirects++; icmp.args.address = args->address; break; case ICMP_ECHO: icmpOutEchos++; break; case ICMP_ECHO_REPLY: icmpOutEchoReps++; break; case ICMP_INFO_RQST: break; case ICMP_INFO_REPLY: break; case ICMP_TIMESTAMP: icmpOutTimestamps++; break; case ICMP_TIME_REPLY: icmpOutTimestampReps++; icmp.args.echo.id = args->echo.id; icmp.args.echo.seq = args->echo.seq; break; case ICMP_ADDR_MASK: icmpOutAddrMasks++; break; case ICMP_ADDR_MASK_REPLY: icmpOutAddrMaskReps++; break; case ICMP_DEST_UNREACH: if(icmp.code == ICMP_FRAG_NEEDED) icmp.args.mtu = args->mtu; icmpOutDestUnreachs++; break; case ICMP_TIME_EXCEED: icmpOutTimeExcds++; break; case ICMP_QUENCH: icmpOutSrcQuenchs++; break; } icmpOutMsgs++; /* Now stick on the ICMP header */ htonicmp(&icmp,&bp); return ip_send(INADDR_ANY,ip->source,ICMP_PTCL,ip->tos,0,&bp,length,0,0); }
/* Pull TCP header off mbuf */ int ntohtcp( struct tcp *tcph, struct mbuf **bpp ){ int hdrlen,i,optlen,kind; register int flags; uint8 hdrbuf[TCPLEN],*cp; uint8 options[TCP_MAXOPT]; memset(tcph,0,sizeof(struct tcp)); i = pullup(bpp,hdrbuf,TCPLEN); /* Note that the results will be garbage if the header is too short. * We don't check for this because returned ICMP messages will be * truncated, and we at least want to get the port numbers. */ tcph->source = get16(&hdrbuf[0]); tcph->dest = get16(&hdrbuf[2]); tcph->seq = get32(&hdrbuf[4]); tcph->ack = get32(&hdrbuf[8]); hdrlen = (hdrbuf[12] & 0xf0) >> 2; flags = hdrbuf[13]; tcph->flags.congest = (flags & 64) ? 1 : 0; tcph->flags.urg = (flags & 32) ? 1 : 0; tcph->flags.ack = (flags & 16) ? 1 : 0; tcph->flags.psh = (flags & 8) ? 1 : 0; tcph->flags.rst = (flags & 4) ? 1 : 0; tcph->flags.syn = (flags & 2) ? 1 : 0; tcph->flags.fin = (flags & 1) ? 1 : 0; tcph->wnd = get16(&hdrbuf[14]); tcph->checksum = get16(&hdrbuf[16]); tcph->up = get16(&hdrbuf[18]); optlen = hdrlen - TCPLEN; /* Check for option field */ if(i < TCPLEN || hdrlen < TCPLEN) return -1; /* Header smaller than legal minimum */ if(optlen == 0) return (int)hdrlen; /* No options, all done */ if(optlen > len_p(*bpp)){ /* Remainder too short for options length specified */ return -1; } pullup(bpp,options,optlen); /* "Can't fail" */ /* Process options */ for(cp=options,i=optlen; i > 0;){ kind = *cp++; i--; /* Process single-byte options */ switch(kind){ case EOL_KIND: return (int)hdrlen; /* End of options list */ case NOOP_KIND: continue; /* Go look for next option */ } /* All other options have a length field */ optlen = *cp++; /* Process valid multi-byte options */ switch(kind){ case MSS_KIND: if(optlen == MSS_LENGTH){ tcph->mss = get16(cp); tcph->flags.mss = 1; } break; case WSCALE_KIND: if(optlen == WSCALE_LENGTH){ tcph->wsopt = *cp; tcph->flags.wscale = 1; } break; case TSTAMP_KIND: if(optlen == TSTAMP_LENGTH){ tcph->tsval = get32(cp); tcph->tsecr = get32(cp+4); tcph->flags.tstamp = 1; } break; } optlen = max(2,optlen); /* Enforce legal minimum */ i -= optlen; cp += optlen - 2; } return (int)hdrlen; }
/* Convert TCP header in host format into mbuf ready for transmission, * link in data (if any). * * If checksum field is zero, recompute it, otherwise take the value * in the host header. */ void htontcp( struct tcp *tcph, struct mbuf **bpp, /* Data in, packet out */ int32 ipsrc, /* For computing header checksum */ int32 ipdest ){ uint16 hdrlen; register uint8 *cp; if(bpp == NULL) return; hdrlen = TCPLEN; if(tcph->flags.mss) hdrlen += MSS_LENGTH; if(tcph->flags.tstamp) hdrlen += TSTAMP_LENGTH; if(tcph->flags.wscale) hdrlen += WSCALE_LENGTH; hdrlen = (hdrlen + 3) & 0xfc; /* Round up to multiple of 4 */ pushdown(bpp,NULL,hdrlen); cp = (*bpp)->data; memset(cp,0,hdrlen); cp = put16(cp,tcph->source); cp = put16(cp,tcph->dest); cp = put32(cp,tcph->seq); cp = put32(cp,tcph->ack); *cp++ = hdrlen << 2; /* Offset field */ *cp = 0; if(tcph->flags.congest) *cp |= 64; if(tcph->flags.urg) *cp |= 32; if(tcph->flags.ack) *cp |= 16; if(tcph->flags.psh) *cp |= 8; if(tcph->flags.rst) *cp |= 4; if(tcph->flags.syn) *cp |= 2; if(tcph->flags.fin) *cp |= 1; cp++; cp = put16(cp,tcph->wnd); cp = put16(cp,tcph->checksum); cp = put16(cp,tcph->up); /* Write options, if any */ if(tcph->flags.mss){ *cp++ = MSS_KIND; *cp++ = MSS_LENGTH; cp = put16(cp,tcph->mss); } if(tcph->flags.tstamp){ *cp++ = TSTAMP_KIND; *cp++ = TSTAMP_LENGTH; cp = put32(cp,tcph->tsval); cp = put32(cp,tcph->tsecr); } if(tcph->flags.wscale){ *cp++ = WSCALE_KIND; *cp++ = WSCALE_LENGTH; *cp++ = tcph->wsopt; } if(tcph->checksum == 0){ /* Recompute header checksum */ struct pseudo_header ph; ph.source = ipsrc; ph.dest = ipdest; ph.protocol = TCP_PTCL; ph.length = len_p(*bpp); put16(&(*bpp)->data[16],cksum(&ph,*bpp,ph.length)); } }