/* * Fill in the reverse lookup question packet */ static __inline void qinit (question_t *q, DWORD ip) { char *c; BYTE i; q->h.ident = 0x5271; /* 7152h = 'qR' */ q->h.flags = intel16 (DRD); /* recursion desired */ q->h.qdcount = intel16 (1); q->h.ancount = 0; q->h.nscount = 0; q->h.arcount = 0; c = q->x; ip = ntohl (ip); for (i = 0; i < 4; ++i) { BYTE x = (BYTE) ip; ip >>= 8; *c = (x < 10) ? 1 : (x < 100) ? 2 : 3; itoa (x,c+1,10); c += *c + 1; } strcpy (c,"\7in-addr\4arpa"); c += 14; *(short*) c = intel16 (DTYPEPTR); c += sizeof(short); *(short*) c = intel16 (DIN); }
int fix_tcp_packet (in_Header *ip, tcp_Header *tcp, int ack_len, int data_len) { static DWORD seq_num = 12345678; /* starting sequence */ tcp_PseudoHeader ph; WORD src_port; tcp->checksum = 0; src_port = tcp->srcPort; tcp->srcPort = tcp->dstPort; tcp->dstPort = src_port; tcp->acknum = intel (intel(tcp->seqnum) + ack_len); tcp->seqnum = intel (seq_num); tcp->offset = sizeof(*tcp) / 4; /* in dwords */ seq_num += data_len; ph.src = ip->source; ph.dst = ip->destination; ph.mbz = 0; ph.protocol = TCP_PROTO; ph.length = intel16 (sizeof(*tcp) + data_len); ph.checksum = CHECKSUM (tcp, sizeof(*tcp) + data_len); ip->length = intel16 (data_len + sizeof(*tcp) + sizeof(*ip)); ip->checksum = 0; ip->checksum = ~CHECKSUM (ip, sizeof(*ip)); tcp->checksum = ~CHECKSUM (&ph, sizeof(ph)); return (sizeof(*ip) + sizeof(*tcp) + data_len); }
static void qinit( void ) { question->h.flags = intel16(DRD); question->h.qdcount = intel16(1); question->h.ancount = 0; question->h.nscount = 0; question->h.arcount = 0; }
/* * Send a tftp acknowledge packet */ static void send_ack (WORD block) { struct tftphdr ack; ack.th_opcode = intel16 (ACK); ack.th_block = intel16 (block); sock_fastwrite (sock, (BYTE*)&ack, TFTP_HEADSIZE); }
static int udp_loopback (udp_Header *udp, unsigned udp_len) { if (intel16(udp->dstPort) == IPPORT_ECHO) { /** \todo UDP loopback handler should handle ECHO protocol */ } else if (intel16(udp->dstPort) == IPPORT_DISCARD) { /** \todo UDP loopback handler should handle DISCARD protocol */ } ARGSUSED (udp_len); return (0); }
static int udp_loopback (udp_Header *udp, unsigned udp_len) { if (intel16(udp->dstPort) == IPPORT_ECHO) { /* !!to-do */ } else if (intel16(udp->dstPort) == IPPORT_DISCARD) { /* !!to-do */ } ARGSUSED (udp_len); return (0); }
/** * Send an "ICMP Time Exceeded" (reassebly timeout) back to 'ip->source' */ int icmp_send_timexceed (const in_Header *ip, const void *mac_dest) { struct _pkt *pkt; union ICMP_PKT *icmp; if (!icmp_check(ip,ICMP_TIMXCEED)) return (0); pkt = (struct _pkt*) _eth_formatpacket (mac_dest, IP4_TYPE); icmp = &pkt->icmp; icmp_print (1, icmp_exceed_str[1], ip->destination); memset (&icmp->unused, 0, sizeof(icmp->unused)); icmp->unused.type = ICMP_TIMXCEED; icmp->unused.code = 1; icmp->unused.ip.hdrlen = sizeof(in_Header) / 4; icmp->unused.ip.ver = 4; icmp->unused.ip.ttl = _default_ttl; icmp->unused.ip.identification = ip->identification; icmp->unused.ip.length = intel16 (sizeof(in_Header)); icmp->unused.ip.proto = ip->proto; icmp->unused.ip.checksum = ~CHECKSUM (&icmp->unused.ip, sizeof(in_Header)); return icmp_send (pkt, ip->destination, ip->source, sizeof(icmp->unused)); }
/* * Send a tftp request packet */ static void send_req (char request, const char *fname) { char *cp, *mode; int len; int fnamlen = strlen (fname); /* The output buffer is setup with the request code, the file name, * and the name of the data format. */ memset (outbuf, 0, sizeof(*outbuf)); outbuf->th_opcode = intel16 (request); len = SEGSIZE - sizeof(outbuf->th_opcode) - strlen(tftp_xfer_mode) - 1; cp = (char*) &outbuf->th_stuff[0]; for ( ; *fname && len > 0 && fnamlen > 0; len--, fnamlen--) *cp++ = *fname++; *cp++ = '\0'; for (mode = tftp_xfer_mode; *mode; ) *cp++ = *mode++; *cp++ = '\0'; /* Finally send the request */ len = (int) (cp - (char*)outbuf); sock_fastwrite (sock, (BYTE*)outbuf, len); }
int sock_loopback (in_Header *ip) { if (ip->proto == TCP_PROTO) { tcp_Header *tcp = (tcp_Header*) ((BYTE*)ip + in_GetHdrLen(ip)); if (intel16(tcp->dstPort) == test_port) { BYTE flags = tcp->flags & tcp_FlagMASK; if (flags == tcp_FlagSYN) { tcp->flags = tcp_FlagACK | tcp_FlagSYN; return fix_tcp_packet (ip, tcp, 1, 0); } if (flags & tcp_FlagACK) { char *data = (char*)tcp + (tcp->offset << 2); if (!strncmp(data,"HELO",4)) { strcpy (data, "Welcome to loop-back handler"); return fix_tcp_packet (ip, tcp, 4, strlen(data) + 1); } if (!strncmp(data,"QUIT",4)) { strcpy (data, "Bye from loop-back handler"); return fix_tcp_packet (ip, tcp, 4, strlen(data) + 1); } } } } return (-1); }
/** * Build a PADx packet. */ static WORD build_pad (struct pppoe_Packet *pkt, WORD code) { BYTE *tags = &pkt->data[0]; if (code == PPPOE_CODE_PADI || code == PPPOE_CODE_PADR) { WORD srv_len = 0; *(WORD*) tags = PPPOE_TAG_SERVICE_NAME; tags += 2; if (cfg.serviceName[0]) { srv_len = strlen (cfg.serviceName); srv_len = min (srv_len, sizeof(pkt->data)-2-PPPOE_TAG_HDR_SIZE); memcpy (tags+2, cfg.serviceName, srv_len); } *(WORD*) tags = intel16 (srv_len); /* Insert PPPOE_TAG_END_LIST ? */ return (srv_len + PPPOE_TAG_HDR_SIZE); } *(WORD*)tags = 0; /* No tags */ return (0); }
/** * Send an ICMP destination/protocol unreachable back to 'ip->source'. * Limit the rate of these to 20 per second. Ref. RFC-1812. */ int icmp_send_unreach (const in_Header *ip, int code) { static DWORD next_time = 0UL; struct _pkt *pkt; union ICMP_PKT *unr; unsigned len; if (!icmp_check(ip,ICMP_UNREACH)) return (0); if (next_time && !chk_timeout(next_time)) return (0); next_time = set_timeout (50); pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP4_TYPE); unr = &pkt->icmp; len = intel16 (ip->length) - in_GetHdrLen (ip); len = min (len, sizeof(*ip)+sizeof(unr->unused.spares)); icmp_print (1, _LANG(icmp_unreach_str[code]), ip->destination); memcpy (&unr->unused.ip, ip, len); unr->unused.type = ICMP_UNREACH; unr->unused.code = (BYTE) code; return icmp_send (pkt, ip->destination, ip->source, sizeof(unr->unused)); }
/* * We have been called with an ip-packet located on stack in * _eth_send_loopback(). Hence, it's safe to call _eth_send() again * here. MAC-header is in front of 'ip'. */ int loopback_device (in_Header *ip) { int ip_hlen, ip_len; DWORD ip_dst; DWORD ip_ofs; WORD ip_flg; if (!loopback_enable || !_chk_ip_header(ip)) /* silently discard */ return (-1); ip_hlen = in_GetHdrLen (ip); /* length of IP-header (w/options) */ ip_len = intel16 (ip->length); /* total length of IP-packet */ ip_dst = ip->destination; ip->destination = ip->source; /* swap source and destination */ ip->source = ip_dst; ip->checksum = 0; ip->checksum = ~checksum (ip, ip_hlen); /* redo check-sum */ ip_ofs = intel16 (ip->frag_ofs); ip_flg = ip_ofs & ~IP_OFFMASK; ip_ofs = (ip_ofs & IP_OFFMASK) << 3; /* 0 <= ip_ofs <= 65536-8 */ if (ip_ofs || (ip_flg & IP_MF)) /* fragment; let _ip_fragment() */ return (ip_len); /* handle it on next poll */ if (ip->proto == ICMP_PROTO) { ICMP_PKT *icmp = (ICMP_PKT*) ((BYTE*)ip + ip_hlen); int len = icmp_loopback (icmp, ip_len - ip_hlen); if (len > 0) return (ip_hlen+len); } else if (ip->proto == UDP_PROTO) { udp_Header *udp = (udp_Header*) ((BYTE*)ip + ip_hlen); int len = udp_loopback (udp, ip_len-ip_hlen); if (len > 0) return (ip_hlen+len); } if (loopback_handler) ip_len = (*loopback_handler) (ip); return (ip_len); }
/* * getresult() - read answer and extract host name * return 0 on error, 1 on success */ static __inline int getresult (udp_Socket *s, char *name) { answer_t a; struct rrpart *rr; char *c; int len = sock_fastread ((sock_type*)s, (BYTE*)&a, sizeof(a)); if (len < sizeof(struct dhead) || a.h.qdcount != intel16(1) || a.h.ancount == 0) return (0); /* Skip question */ c = a.x; while (*c) ++c; c += 5; /* Skip name */ while (*c) { if ((*c & 0xC0) == 0xC0) { c += 2; break; } else ++c; } rr = (struct rrpart *) c; if (rr->rtype != intel16(DTYPEPTR)) return (0); c = (char*) &rr->rdata; while (*c) { if ((*c & 0xC0) == 0xC0) c = a.x + (*(int*)c & 0x3FFF); strncpy (name,c+1,*c); name += *c; c += *c + 1; if (*c) *name++ = '.'; } *name = 0; return (1); }
/** * Handle incoming PPPoE packets. */ int pppoe_handler (const pppoe_Packet *pkt) { const BYTE *buf; const void *src; const void *dst; WORD proto, len; BOOL bcast, delivered = FALSE; if (pkt->type != 1 || pkt->ver != 1) return (0); src = MAC_SRC (pkt); dst = MAC_DST (pkt); proto = MAC_TYP (pkt); bcast = !memcmp (dst, _eth_brdcast, _eth_mac_len); if (proto == PPPOE_SESS_TYPE && state == StateSession) { if (pkt->code == PPPOE_CODE_SESS && pkt->session == session && !bcast && !memcmp(dst, _eth_addr, _eth_mac_len) && /* to us? */ !memcmp(src, ac_macAddr, _eth_mac_len)) { len = intel16 (pkt->length); buf = &pkt->data[0]; ppp_input (buf, len); /* assume ppp_input() traces it */ delivered = TRUE; } } else if (!bcast && proto == PPPOE_DISC_TYPE && state == StateDiscovery) { if (pkt->code == PPPOE_CODE_PADO) /* Offer (can this be bcast?) */ { got_PADO = TRUE; memcpy (ac_macAddr, src, _eth_mac_len); } else if (pkt->code == PPPOE_CODE_PADT && /* Terminate */ pkt->session == session) { if (cfg.trace) outsnl (_LANG("PPPoE: session terminated")); got_PADT = TRUE; session = 0; } else if (pkt->code == PPPOE_CODE_PADS) /* Session-confirmation */ { got_PADS = TRUE; session = pkt->session; } else if (pkt->code == PPPOE_CODE_PADM) /* Message (what to do?) */ { got_PADM = TRUE; } } if (!delivered) DEBUG_RX (NULL, pkt); return (1); }
/* ddextract * extract the data from a response message. * returns the appropriate status code and if the data is available, * copies it into data * dtype is the RR type; unpacker is the function to get the data * from the RR. */ static longword ddextract( struct useek *qp, byte dtype, unpacker_funct unpacker, void *data ) { word i,j,nans,rcode; struct rrpart *rrp; byte *p,space[260]; nans = intel16(qp->h.ancount); /* number of answers */ rcode = DRCODE & intel16(qp->h.flags); /* return code for this message*/ if (rcode > 0) return(rcode); if (nans > 0 && /* at least one answer */ (intel16(qp->h.flags) & DQR)) { /* response flag is set */ p = (byte *)&qp->x; /* where question starts */ i = unpackdom((char*)space,(char*)p,(char*)qp); /* unpack question name */ /* spec defines name then QTYPE + QCLASS = 4 bytes */ p += i+4; /* * at this point, there may be several answers. We will take the first * one which has an IP number. There may be other types of answers that * we want to support later. */ while (nans-- > 0) { /* look at each answer */ i = unpackdom((char*)space,(char*)p,(char*)qp); /* answer name to unpack */ /* n_puts(space);*/ p += i; /* account for string */ rrp = (struct rrpart *)p; /* resource record here */ /* * check things which might not align on 68000 chip one byte at a time */ if (!*p && *(p+1) == dtype && /* correct type and class */ !*(p+2) && *(p+3) == DIN) { unpacker(data,rrp,qp); /* get data we're looking for */ return(0); /* successful return */ } movmem(&rrp->rdlength,&j,2); /* 68000 alignment */ p += 10+intel16(j); /* length of rest of RR */ } } return(-1); /* generic failed to parse */ }
/** * Build and send a PPPoE Session packet (IPCP or LCP packet). */ int pppoe_send_sess (const void *sock, const BYTE *buf, WORD len) { pppoe_Packet *pkt; pkt = (pppoe_Packet*) _eth_formatpacket (&ac_macAddr[0], PPPOE_SESS_TYPE); pkt->ver = 1; pkt->type = 1; pkt->code = PPPOE_CODE_SESS; pkt->session = session; pkt->length = intel16 (len); memcpy (pkt->data, buf, len); return _eth_send (len + PPPOE_HDR_SIZE, sock, __FILE__, __LINE__); }
/** * Build a PPPoE session packet header. * Only called for IPv4 packets. */ void *pppoe_mac_format (union link_Packet *tx) { pppoe_Packet *pppoe = (pppoe_Packet*) tx->eth.data; memcpy (&tx->eth.head.destination[0], ac_macAddr, sizeof(mac_address)); memcpy (&tx->eth.head.source[0], _eth_addr, sizeof(mac_address)); tx->eth.head.type = PPPOE_SESS_TYPE; pppoe->ver = 1; pppoe->type = 1; pppoe->code = PPPOE_CODE_SESS; pppoe->session = session; _pkt_ip_ofs = sizeof(eth_Header) + PPPOE_HDR_SIZE + 2; /* !! see _eth_send() */ *(WORD*) &pppoe->data[0] = intel16 (PPP_IP); return (void*) &pppoe->data[2]; }
static int _rarp_request (void) { rarp_Header *rarp = (rarp_Header*) _eth_formatpacket (&_eth_brdcast[0], RARP_TYPE); rarp->hwType = intel16 (_eth_get_hwtype(NULL,NULL)); rarp->protType = IP4_TYPE; rarp->hwAddrLen = sizeof (mac_address); rarp->protoAddrLen = sizeof (my_ip_addr); rarp->opcode = RARP_REQUEST; rarp->srcIPAddr = 0; rarp->dstIPAddr = 0; memcpy (rarp->srcEthAddr, _eth_addr, sizeof(mac_address)); memcpy (rarp->dstEthAddr, _eth_addr, sizeof(mac_address)); return _eth_send (sizeof(*rarp), NULL, __FILE__, __LINE__); }
// The following code was leveraged from ip_handler() and tcp_handler() far ll_prefix *my_ports(far ll_prefix *LL, byte *hdrbuf, word *pflags) { int myport; // The port where we received this message word iplen; in_Header *ip; tcp_Header *tp; // We will always be processing the frame with the TCP/IP stack *pflags |= CUSTOM_PKT_FLAG_PROCESS; // Check whether the socket is in use if (! sock_alive(&socket) ) { // Socket is free, see if this is a packet bound for one of the ports // we're willing to receive on. // First, parse the packet (code leveraged from ip_handler(), // tcp_handler().) _pkt_buf2root(LL, ip = (in_Header *)(hdrbuf+LL->net_offs), sizeof(in_Header), LL->net_offs); iplen = in_GetHdrlenBytes(ip); if (ip->proto == TCP_PROTO) { // We have a valid TCP packet, check myport _pkt_buf2root(LL, tp = (tcp_Header *)(hdrbuf+LL->tport_offs), sizeof(tcp_Header), LL->tport_offs); // We received the packet on the packet's destination port myport = intel16(tp->dstPort); switch(myport) { // List of ports which we're willing to receive. We could // also search through a list, use a range (if (myport > 23 && // myport < 30)), etc. case 7: // Echo protocol case 23: // Telnet protocol case 80: // HTTP protocol // Accept the connection by calling listen for the incoming // port tcp_listen(&socket,myport,0,0,NULL,0); break; default: // Do nothing -- don't accept the connection } }
/** * Build and send a PADx (PPPoE Active Discovery) packet as * link-layer broadcast or unicast. */ static int pppoe_send_disc (int code) { pppoe_Packet *pkt; WORD len; pkt = (pppoe_Packet*) _eth_formatpacket (&ac_macAddr[0], PPPOE_DISC_TYPE); len = build_pad (pkt, code); pkt->ver = 1; pkt->type = 1; pkt->code = code; pkt->session = session; pkt->length = intel16 (len); #if defined(USE_DEBUG) if (cfg.trace) (*_printf) ("PPPOE: sending %s\n", pppoe_get_code(code)); #endif return _eth_send (len + PPPOE_HDR_SIZE, NULL, __FILE__, __LINE__); }
/* * Send an ICMP destination/protocol unreachable back to ip->source */ void icmp_unreach (const in_Header *ip, int code) { struct _pkt *pkt; union icmp_pkt *unr; int len; if (!icmp_chk_src(ip,ICMP_UNREACH)) return; pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP_TYPE); unr = &pkt->icmp; len = intel16 (ip->length) - in_GetHdrLen (ip); len = min (len, sizeof(*ip)+sizeof(unr->unused.spares)); icmp_print (1, icmp_unreach_str[code], ip->destination); memcpy (&unr->unused.ip, ip, len); unr->unused.type = ICMP_UNREACH; unr->unused.code = code; icmp_send (pkt, ip->destination, ip->source, sizeof(unr->unused)); }
/* * Send an "ICMP Time Exceeded" (reassebly timeout) back to 'ip->source' */ void icmp_timexceed (const in_Header *ip, const void *mac_dest) { struct _pkt *pkt; union icmp_pkt *tim; int len; if (!icmp_chk_src(ip,ICMP_TIMXCEED)) return; pkt = (struct _pkt*) _eth_formatpacket (mac_dest, IP_TYPE); tim = &pkt->icmp; len = intel16 (ip->length) - in_GetHdrLen (ip); len = min (len, sizeof(*ip) + sizeof(tim->unused.spares)); icmp_print (1, icmp_exceed_str[1], ip->destination); memcpy (&tim->unused.ip, ip, len); tim->unused.type = ICMP_TIMXCEED; tim->unused.code = 1; icmp_send (pkt, ip->destination, ip->source, sizeof(tim->unused)); }
/* sendom * put together a domain lookup packet and send it * uses port 53 * num is used as identifier */ static int sendom( char *s, longword towho, word num, byte dtype ) { word i,ulen; byte *psave,*p; psave = (byte*)&(question->x); i = packdom((char *)&(question->x),s); p = &(question->x[i]); *p++ = 0; /* high byte of qtype */ *p++ = dtype; /* number is < 256, so we know high byte=0 */ *p++ = 0; /* high byte of qclass */ *p++ = DIN; /* qtype is < 256 */ question->h.ident = intel16(num); ulen = sizeof(struct dhead)+(p-psave); udp_open( dom_sock, 997, towho, 53, NULL ); /* divide err */ sock_write( (sock_type*)dom_sock, (byte*)question, ulen ); return( ulen); }
/* * Open a TFTP connection on a random local port (our transaction ID). * Send the request, wait for first data block and send the first ACK. */ static int tftp_open (DWORD server, const char *fname) { int retry; WORD port = 69; #if defined(USE_BSD_API) struct servent *sp = getservbyname ("tftp", "udp"); if (sp) port = intel16 ((WORD)sp->s_port); #endif currblock = 0UL; blocksize = 0; for (retry = 0; retry < tftp_retry; retry++) { WORD our_tid; /* our transaction ID (local port) */ if (tftp_lport && tftp_lport < TFTP_PORT_LOW) outsnl (_LANG("tftp: Illegal local port.")); if (tftp_lport >= TFTP_PORT_LOW) our_tid = tftp_lport; else our_tid = Random (TFTP_PORT_LOW, TFTP_PORT_HIGH); /* Try to open a TFTP connection to the server */ if (!udp_open(&sock->udp, our_tid, server, port, NULL)) { TRACE (("tftp: %s\n", sockerr(sock))); return (0); } sock->udp.locflags |= LF_NOCLOSE; /* don't close socket on timeout */ /* Send the file request block, and then wait for the first data * block. If there is no response to the query, retry it with * another transaction ID (local port), so that all old packets get * discarded automatically. */ send_req (RRQ, fname); /* This hack makes it work because the response is sent back on * a source-port different from port 69; i.e. the server TID * uses a random port. Force the response packet to match a passive * socket in udp_handler(). */ sock->udp.hisaddr = 0; ibuflen = recv_packet (1); if (ibuflen >= 0) { blocksize = ibuflen; isopen = TRUE; send_ack (1); return (1); } /* If an error (except timeout) occurred, retries are useless */ if (tftp_errno == ERR_ERR || tftp_errno == ERR_UNKNOWN) break; } return (0); }
/** * Poll for arrival of new packets (IP/ARP/RARP/PPPoE protocol). * Sets protocol-type of packet received in 'type'. * * For Ethernet/TokenRing-type drivers: \n * \retval Pointer past the MAC-header to the IP/ARP/RARP * protocol header. Also check for link-layer broadcast. * * For PPP/SLIP-type drivers (no MAC-headers): \n * \retval Pointer to the IP-packet itself. * IP-protocol is assumed. Can never be link-layer broadcast. */ void *_eth_arrived (WORD *type_ptr, BOOL *broadcast) { union link_Packet *pkt; mac_address *dst; void *ret = NULL; BOOL is_bcast = FALSE; WORD type = 0; SIO_TRACE (("_eth_arrived")); if (!_eth_is_init) /* GvB 2002-09, Lets us run without a working driver */ return (NULL); if (_eth_recv_hook) pkt = (union link_Packet*) (*_eth_recv_hook) (&type); else pkt = poll_recv_queue (&type); if (!pkt) return (NULL); if (_eth_recv_peek && !(*_eth_recv_peek)(pkt)) { _eth_free (pkt); return (NULL); } /* Hack: If _ip?_handler() can't be reentered, only accept * non-IP packets. Assume PPPoE session packets carry only IP. */ if (_ip_recursion && (type == IP4_TYPE || type == IP6_TYPE || type == PPPOE_SESS_TYPE)) { /**< \todo push back packet, else it's lost */ STAT (macstats.num_ip_recurse++); _eth_free (pkt); return (NULL); } if (_pktserial) { dst = NULL; ret = (void*) &pkt->ip; } else if (_pktdevclass == PDCLASS_TOKEN || _pktdevclass == PDCLASS_TOKEN_RIF) { dst = &pkt->tok.head.destination; ret = (void*) &pkt->tok.data; } else if (_pktdevclass == PDCLASS_FDDI) { dst = &pkt->fddi.head.destination; ret = (void*) &pkt->fddi.data; } else if (_pktdevclass == PDCLASS_ARCNET) { dst = (mac_address*) &pkt->arc.head.destination; ret = (void*) ((BYTE*)pkt + ARC_HDRLEN); } else /* must be ether */ { dst = &pkt->eth.head.destination; ret = (void*) &pkt->eth.data; } #if defined(NEED_PKT_SPLIT) pkt_split_mac_in (pkt); #endif #if defined(USE_STATISTICS) update_in_stat(); #endif #if defined(NEED_PKT_SPLIT) && defined(USE_DEBUG) && 0 /* test */ pkt_print_split_in(); #endif /* ARCnet should never have LLC fields. So don't test for it */ if (_pktdevclass != PDCLASS_ARCNET) { if (dst && !memcmp(dst, &_eth_brdcast, sizeof(_eth_brdcast))) is_bcast = TRUE; if (intel16(type) < 0x600) /* LLC length field */ fix_llc_head (&ret); } else if (dst && *(BYTE*)dst == 0) /* ARCnet broadcast */ { is_bcast = TRUE; } if (type_ptr) *type_ptr = type; if (broadcast) *broadcast = is_bcast; return (ret); }
WORD ntohs (WORD val) { return intel16(val); }
WORD htons (WORD val) { return intel16(val); }
/** * _eth_send() does the actual transmission once we are complete with * filling the buffer. Do any last minute patches here, like fix the * size. Send to "loopback" device if it's IP and destination matches * loopback network (127.x.x.x.). * * Return length of network-layer packet (not length of link-layer * packet). */ int _eth_send (WORD len, const void *sock, const char *file, unsigned line) { #if defined(USE_DEBUG) || defined(USE_LOOPBACK) unsigned errline = 0; #endif BOOL send_loopback_to_driver = FALSE; SIO_TRACE (("_eth_send, len %d", len)); if (!_eth_is_init) /* GvB 2002-09, Lets us run without a working eth */ { SOCK_ERRNO (ENETDOWN); return (0); } #if defined(WIN32) /* * Just a test for now; send it to the driver and look what happens.... * They go on the wire and not to the Winsock loopback provider. * No surprise here yet. */ if (loopback_mode & LBACK_MODE_WINSOCK) send_loopback_to_driver = TRUE; #endif if (proto == IP4_TYPE) { /* Sending to loopback device if IPv4. */ const in_Header *ip = (const in_Header*) nw_pkt; if (!send_loopback_to_driver && _ip4_is_loopback_addr(intel(ip->destination))) { #if defined(USE_LOOPBACK) len = send_loopback (*TX_BUF(), FALSE, &errline); #else STAT (ip4stats.ips_odropped++); /* packet dropped (null-device) */ #endif goto debug_tx; } } #if defined(USE_IPV6) else if (proto == IP6_TYPE) { const in6_Header *ip = (const in6_Header*) nw_pkt; if (!send_loopback_to_driver && IN6_IS_ADDR_LOOPBACK(&ip->destination)) { #if defined(USE_LOOPBACK) len = send_loopback (*TX_BUF(), TRUE, &errline); #else STAT (ip6stats.ip6s_odropped++); #endif goto debug_tx; } } #endif /* USE_IPV6 */ #if defined(USE_PPPOE) else if (proto == PPPOE_SESS_TYPE) { pppoe_Packet *pppoe = (pppoe_Packet*) TX_BUF()->eth.data; pppoe->length = intel16 (len+2); len += PPPOE_HDR_SIZE + 2; /* add 2 for protocol */ } #endif /* Store the last Tx CPU timestamp (for debugging). */ #if (DOSX) && !(DOSX & WINWATT) if (_dbugxmit && has_rdtsc) get_rdtsc2 (&_eth_last.tx.tstamp); #endif /* Do the MAC-dependant transmit. `len' on return is total length * of link-layer packet sent. `len' is 0 on failure. The xmit-hook * is used by e.g. libpcap/libnet. */ if (_eth_xmit_hook) len = (*_eth_xmit_hook) (TX_BUF(), len + _pkt_ip_ofs); else len = (*mac_transmit) (TX_BUF(), len + _pkt_ip_ofs); if (len > _pkt_ip_ofs) { _eth_last.tx.size = len; len -= _pkt_ip_ofs; } else { if (debug_on) outs ("Tx failed. "); len = 0; _eth_last.tx.size = 0; } debug_tx: #if defined(NEED_PKT_SPLIT) pkt_split_mac_out (TX_BUF()); #endif #if defined(USE_STATISTICS) if (len > 0) update_out_stat(); #endif #if defined(USE_DEBUG) if (_dbugxmit) (*_dbugxmit) (sock, (const in_Header*)nw_pkt, file, line); if (len == 0) { if (errline && !send_loopback_to_driver) dbug_printf ("** Error in loopback handler, line %u\n", errline); else { const char err[] = "** Transmit fault **\n"; TCP_CONSOLE_MSG (0, ("%s", err)); dbug_printf (err); } } #else ARGSUSED (sock); ARGSUSED (file); ARGSUSED (line); #endif /* Undo hack done in pppoe_mac_format() */ if (proto == PPPOE_SESS_TYPE || _pktdevclass == PDCLASS_ETHER) _pkt_ip_ofs = sizeof(eth_Header); return (len); }
/* * Handler for incoming ICMP packets */ void icmp_handler (const in_Header *ip, BOOL broadcast) { union icmp_pkt *icmp; in_Header *orig_ip; int len, type, code; BOOL for_me, i_orig; /* is it for me, did I originate it */ const char *msg; DEBUG_RX (NULL, ip); if (block_icmp) /* application is handling ICMP; not needed */ return; len = in_GetHdrLen (ip); icmp = (union icmp_pkt*) ((BYTE*)ip + len); len = intel16 (ip->length) - len; for_me = (DWORD) (intel(ip->destination) - my_ip_addr) <= multihomes; if (!for_me || broadcast) /* drop broadcast pings.. */ return; if (len < sizeof(icmp->info)) { STAT (icmpstats.icps_tooshort++); return; } if (checksum(icmp,len) != 0xFFFF) { STAT (icmpstats.icps_checksum++); icmp_print (1, _LANG("bad checksum"), ip->source); return; } type = icmp->unused.type; code = icmp->unused.code; orig_ip = &icmp->ip.ip; i_orig = is_local_addr (intel(orig_ip->source)); if (type == ICMP_MASKREPLY) { if (!_domask_req) return; i_orig = TRUE; } /* !! this needs work */ if (!i_orig && (type != ICMP_ECHOREPLY && type != ICMP_ECHO && type != ICMP_IREQREPLY && type != ICMP_TSTAMP)) { icmp_bogus (ip, type, NULL); return; } switch (type) { case ICMP_ECHOREPLY: /* check if we were waiting for it */ STAT (icmpstats.icps_inhist[ICMP_ECHOREPLY]++); ping_hcache = intel (ip->source); ping_tcache = set_timeout (1000) - *(DWORD*)&icmp->echo.identifier; if (ping_tcache > 0x7FFFFFFFL) ping_tcache += 0x1800B0L; ping_number = *(DWORD*)(((BYTE*)&icmp->echo.identifier) + 4); return; case ICMP_UNREACH: STAT (icmpstats.icps_inhist[ICMP_UNREACH]++); if (code < DIM(icmp_unreach_str)) { icmp_print (1, msg = icmp_unreach_str[code], ip->source); #if !defined(USE_UDP_ONLY) if (orig_ip->proto == TCP_PROTO) _tcp_cancel (orig_ip, type, msg, 0); else #endif if (orig_ip->proto == UDP_PROTO) _udp_cancel (orig_ip, type, msg, 0); } else STAT (icmpstats.icps_badcode++); return; case ICMP_SOURCEQUENCH: STAT (icmpstats.icps_inhist[ICMP_SOURCEQUENCH]++); #if !defined(USE_UDP_ONLY) if (orig_ip->proto == TCP_PROTO) { icmp_print (1, _LANG("Source Quench"), ip->source); _tcp_cancel (orig_ip, type, NULL, 0); } #endif return; case ICMP_REDIRECT: STAT (icmpstats.icps_inhist[ICMP_REDIRECT]++); if (code < 4) { DWORD new_gw = intel (icmp->ip.ipaddr); /* Check if new gateway is on our subnet */ if ((new_gw ^ my_ip_addr) & sin_mask) { char buf[100], adr[20]; strcpy (buf, ", GW = "); strcat (buf, _inet_ntoa(adr,new_gw)); icmp_bogus (ip, type, buf); return; } icmp_print (1, msg = icmp_redirect_str[code], ip->source); switch (orig_ip->proto) { #if !defined(USE_UDP_ONLY) case TCP_PROTO: if (do_redirect.tcp) /* do it to some socket */ _tcp_cancel (orig_ip, type, msg, new_gw); break; #endif case UDP_PROTO: if (do_redirect.udp) _udp_cancel (orig_ip, type, msg, new_gw); break; case ICMP_PROTO: if (do_redirect.icmp) { _ip_recursion = 1; _arp_register (new_gw, intel(orig_ip->destination), 0); _ip_recursion = 0; } break; case IGMP_PROTO: if (do_redirect.igmp) { _ip_recursion = 1; _arp_register (new_gw, intel(orig_ip->destination), 0); _ip_recursion = 0; } break; } } else STAT (icmpstats.icps_badcode++); return; case ICMP_ECHO: STAT (icmpstats.icps_inhist[ICMP_ECHO]++); icmp_print (2, _LANG("PING requested of us"), ip->source); { /* Extract eth-address and create Echo reply packet. */ struct _pkt *pkt; union icmp_pkt *newicmp; if (!icmp_chk_src(ip,ICMP_ECHO)) return; pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP_TYPE); newicmp = &pkt->icmp; /* Don't let a huge reassembled ICMP-packet kill us. */ len = min (len, mtu - sizeof(*ip)); memcpy (newicmp, icmp, len); newicmp->echo.type = ICMP_ECHOREPLY; newicmp->echo.code = code; /* Use supplied ip values in case we ever multi-home. * Note that ip values are still in network order. */ icmp_send (pkt, ip->destination, ip->source, len); icmp_print (2, _LANG("PING reply sent"), 0); } return; case ICMP_TIMXCEED: if (code >= DIM(icmp_exceed_str)) { STAT (icmpstats.icps_badcode++); return; } STAT (icmpstats.icps_inhist[ICMP_TIMXCEED]++); if (code != 1) switch (orig_ip->proto) { #if !defined(USE_UDP_ONLY) case TCP_PROTO: icmp_print (1, icmp_exceed_str[code], ip->source); _tcp_cancel (orig_ip, ICMP_TIMXCEED, NULL, 0); break; #endif case UDP_PROTO: icmp_print (1, icmp_exceed_str[code], ip->source); _udp_cancel (orig_ip, ICMP_TIMXCEED, NULL, 0); break; } return; case ICMP_PARAMPROB: STAT (icmpstats.icps_inhist[ICMP_PARAMPROB]++); switch (orig_ip->proto) { #if !defined(USE_UDP_ONLY) case TCP_PROTO: icmp_print (0, _LANG(icmp_type_str[type]), ip->source); _tcp_cancel (orig_ip, type, NULL, 0); break; #endif case UDP_PROTO: icmp_print (0, _LANG(icmp_type_str[type]), ip->source); _udp_cancel (orig_ip, type, NULL, 0); break; } return; case ICMP_ROUTERADVERT: /* todo !! */ STAT (icmpstats.icps_inhist[ICMP_ROUTERADVERT]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); return; case ICMP_ROUTERSOLICIT: /* todo !! */ STAT (icmpstats.icps_inhist[ICMP_ROUTERSOLICIT]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); return; case ICMP_TSTAMP: STAT (icmpstats.icps_inhist[ICMP_TSTAMP]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); /* todo!!, send reply? */ return; case ICMP_TSTAMPREPLY: STAT (icmpstats.icps_inhist[ICMP_TSTAMPREPLY]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); /* todo!!, should store */ return; case ICMP_IREQ: STAT (icmpstats.icps_inhist[ICMP_IREQ]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); /* todo!!, send reply */ return; case ICMP_IREQREPLY: STAT (icmpstats.icps_inhist[ICMP_IREQREPLY]++); icmp_print (1, _LANG(icmp_type_str[type]), ip->source); /* todo!!, send reply upwards */ return; case ICMP_MASKREQ: STAT (icmpstats.icps_inhist[ICMP_MASKREQ]++); break; case ICMP_MASKREPLY: STAT (icmpstats.icps_inhist[ICMP_MASKREPLY]++); icmp_print (0, _LANG(icmp_type_str[type]), ip->source); if ((icmp->mask.identifier == addr_mask_id) && (icmp->mask.sequence == addr_mask_seq-1) && sin_mask != intel(icmp->mask.mask)) outsnl ("Conflicting net-mask from \"ICMP Addr Mask Reply\"\7"); addr_mask_id = 0; return; } }
/** * \def loopback_device(). * * We have been called with an IP-packet located on stack by `send_loopback()' * (in pcsed.c). Hence, it's safe to call _eth_send() again here. * MAC-header is in front of 'ip'. */ int loopback_device (in_Header *ip) { int ip_len = 0; DWORD ip_dst; DWORD ip_ofs; WORD ip_flg; if (!(loopback_mode & LBACK_MODE_ENABLE)) return (-1); if (ip->ver == 4) { int ip_hlen; if (!_chk_ip4_header(ip)) /* silently discard */ return (-1); ip_hlen = in_GetHdrLen (ip); /* length of IP-header (w/options) */ ip_len = intel16 (ip->length); /* total length of IP-packet */ ip_dst = ip->destination; ip->destination = ip->source; /* swap source and destination */ ip->source = ip_dst; ip->checksum = 0; ip->checksum = ~CHECKSUM (ip, ip_hlen); /* redo check-sum */ ip_ofs = intel16 (ip->frag_ofs); ip_flg = (WORD) (ip_ofs & ~IP_OFFMASK); ip_ofs = (ip_ofs & IP_OFFMASK) << 3; /* 0 <= ip_ofs <= 65536-8 */ if (ip_ofs || (ip_flg & IP_MF)) /* fragment; let ip4_defragment() */ return (ip_len); /* handle it on next poll */ if (ip->proto == ICMP_PROTO) { ICMP_PKT *icmp = (ICMP_PKT*) ((BYTE*)ip + ip_hlen); int len = icmp_loopback (icmp, ip_len - ip_hlen); if (len > 0) return (ip_hlen+len); } else if (ip->proto == UDP_PROTO) { udp_Header *udp = (udp_Header*) ((BYTE*)ip + ip_hlen); int len = udp_loopback (udp, ip_len-ip_hlen); if (len > 0) return (ip_hlen+len); } } #if defined(USE_IPV6) else if (ip->ver == 6) { in6_Header *ip6 = (in6_Header*)ip; ip6_address ip6_dst; ip_len = intel16 (ip6->len); memcpy (&ip6_dst, &ip6->destination, sizeof(ip6_dst)); memcpy (&ip6->destination, &ip6->source, sizeof(ip6->destination)); memcpy (&ip6->source, &ip6_dst, sizeof(ip6->source)); if (ip6->next_hdr == IP6_NEXT_ICMP) { ICMP_PKT *icmp = (ICMP_PKT*) (ip6 + 1); int len = icmp_loopback (icmp, ip_len); if (len > 0) return (len + sizeof(*ip6)); } else if (ip6->next_hdr == UDP_PROTO) { udp_Header *udp = (udp_Header*) (ip6 + 1); int len = udp_loopback (udp, ip_len); if (len > 0) return (len + sizeof(*ip6)); } } else { (*_printf) ("%s: Illegal IP-packet (ver %d) for loopback device\n", __FILE__, ip->ver); return (-1); } #endif if (loopback_handler) ip_len = (*loopback_handler) (ip); return (ip_len); }