static int icmp_echo_reply (const in_Header *ip, const union ICMP_PKT *req, unsigned len) { struct _pkt *pkt; union ICMP_PKT *icmp; if (!icmp_check(ip,ICMP_ECHO)) return (0); icmp_print (2, _LANG("PING reply sent"), 0); #if defined(USE_FRAGMENTS) if (len > _mtu - sizeof(*ip)) { icmp = (union ICMP_PKT*) req; /* reuse input for output */ icmp->echo.type = ICMP_ECHOREPLY; icmp->echo.checksum = 0; icmp->echo.checksum = ~CHECKSUM (icmp, len); return _IP4_SEND_FRAGMENTS (NULL, ICMP_PROTO, ip->source, icmp, len); } #endif pkt = (struct _pkt*) _eth_formatpacket (MAC_SRC(ip), IP4_TYPE); icmp = &pkt->icmp; len = min (len, _mtu - sizeof(*ip)); memcpy (icmp, req, len); icmp->echo.type = ICMP_ECHOREPLY; icmp->echo.code = req->echo.code; /* Win uses 0, Unix !0 */ /* Use supplied ip values in case we ever multi-home. * Note that ip values are still in network order. */ return icmp_send (pkt, ip->destination, ip->source, len); }
/* * igmp_report - send a IGMP Report packet * * int igmp_report (DWORD ip) * Where: * ip is the IP address to report. * * Returns: * 0 if unable to send report * 1 report was sent successfully */ int igmp_report (DWORD ip) { struct IGMP_PKT *pkt; IGMP_packet *igmp; eth_address ethaddr; /* get the ethernet addr of the destination */ multi_to_eth ((DWORD)ALL_SYSTEMS, (BYTE*)ðaddr); /* format the packet with the request's hardware address */ pkt = (struct IGMP_PKT*) _eth_formatpacket (ethaddr, IP_TYPE); igmp = &pkt->igmp; ip = intel (ip); /* fill in the igmp packet */ igmp->type = IGMP_REPORT; igmp->version = IGMP_VERSION; igmp->mbz = 0; igmp->address = ip; igmp->checksum = 0; igmp->checksum = ~checksum (igmp,sizeof(*igmp)); return IP_OUTPUT (&pkt->in, 0, ip, IGMP_PROTO, 0, 0, 0, (int)sizeof(*igmp), NULL); }
/** * 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 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)); }
void *icmp_Format (DWORD host) { mac_address dest; /* We use ARP rather than supplied hardware address. * After first ping this will still be in cache. */ if (!_arp_resolve(host,&dest,0)) /* unable to find address */ return (NULL); return _eth_formatpacket (&dest, IP_TYPE); }
void icmp_mask_req (void) { mac_address *dst = (_pktserial ? NULL : &_eth_brdcast); struct _pkt *pkt = (struct _pkt*) _eth_formatpacket (dst, IP_TYPE); union icmp_pkt *req = &pkt->icmp; addr_mask_id = (WORD) set_timeout (0); /* get a random ID */ req->mask.type = ICMP_MASKREQ; req->mask.code = 0; req->mask.identifier = addr_mask_id; req->mask.sequence = addr_mask_seq++; req->mask.mask = 0; icmp_send (pkt, 0, (DWORD)INADDR_BROADCAST, sizeof(req->mask)); }
/** * 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__); }
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__); }
/** * 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 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)); }
/* * 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)); }
static int ping_gateway (DWORD host, void *eth) { struct ping_pkt *pkt; struct icmp_echo *icmp; struct in_Header *ip; int len; pkt = (struct ping_pkt*) _eth_formatpacket (eth, IP4_TYPE); ip = &pkt->in; icmp = &pkt->icmp; len = sizeof (*icmp); icmp_id = (WORD) set_timeout (0); /* "random" id */ icmp->type = ICMP_ECHO; icmp->code = 0; icmp->index = 1; icmp->identifier = icmp_id; icmp->sequence = icmp_seq++; icmp->checksum = 0; icmp->checksum = ~CHECKSUM (icmp, len); return IP4_OUTPUT (ip, 0, intel(host), ICMP_PROTO, 1, (BYTE)_default_tos, 0, len, NULL); }
/* * Raw IP transmitter */ static int ip_transmit (Socket *socket, const void *tx, int len) { eth_address eth; u_long dest; unsigned tx_len, tx_room; sock_type *sk = (sock_type*)socket->udp_sock; struct ip *ip = (struct ip*) tx; const BYTE *buf = (const BYTE*) tx; WORD flags = 0; DWORD offset; UINT h_len, o_len; tcp_tick (NULL); /* process other TCBs too */ tcp_Retransmitter (1); /* This should never happen */ if (ip && (socket->so_state & SS_NBIO) && sock_tbleft(sk) < (len + socket->send_lowat)) { SOCK_DEBUGF ((socket, ", EWOULDBLOCK")); SOCK_ERR (EWOULDBLOCK); return (-1); } if (ip) { offset = ntohs (ip->ip_off); flags = offset & ~IP_OFFMASK; offset = (offset & IP_OFFMASK) << 3; /* 0 <= ip_ofs <= 65536-8 */ } SOCK_DEBUGF ((socket, ", %s / Raw", inet_ntoa(socket->remote_addr->sin_addr))); if (ip && (socket->inp_flags & INP_HDRINCL)) { dest = ip->ip_dst.s_addr; tx_len = len; tx_room = mtu; } else { dest = socket->remote_addr->sin_addr.s_addr; tx_len = len + sizeof (*ip); tx_room = mtu + sizeof (*ip); } if (!dest || !_arp_resolve(ntohl(dest),ð,0)) { SOCK_DEBUGF ((socket, ", no route")); SOCK_ERR (EHOSTUNREACH); STAT (ipstats.ips_noroute++); return (-1); } #if defined(USE_FRAGMENTS) if (!(socket->inp_flags & INP_HDRINCL) && tx_len + socket->ip_opt_len > tx_room) { sk = (sock_type*)socket->raw_sock; if (flags & IP_DF) { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_toolong++); return (-1); } return SEND_IP_FRAGMENTS (sk, sk->raw.ip_type, dest, buf, len); } #else if (!(socket->inp_flags & INP_HDRINCL) && tx_len + socket->ip_opt_len > tx_room) { SOCK_DEBUGF ((socket, ", EMSGSIZE")); SOCK_ERR (EMSGSIZE); STAT (ipstats.ips_toolong++); return (-1); } #endif ip = (struct ip*) _eth_formatpacket (ð, IP_TYPE); if (socket->inp_flags & INP_HDRINCL) { memcpy (ip, buf, len); if (ip->ip_src.s_addr == 0) { ip->ip_src.s_addr = gethostid(); ip->ip_sum = 0; ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2); } if (ip->ip_sum == 0) ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2); } else { if (socket->ip_opt && socket->ip_opt_len > 0) { BYTE *data; o_len = min (socket->ip_opt_len, sizeof(socket->ip_opt->ip_opts)); h_len = sizeof(*ip) + o_len; data = (BYTE*)ip + h_len; memcpy (ip+1, &socket->ip_opt->ip_opts, o_len); memcpy (data, buf, len); tx_len += o_len; if (socket->ip_opt->ip_dst.s_addr) /* using source routing */ dest = socket->ip_opt->ip_dst.s_addr; } else { if (buf) memcpy (ip+1, buf, len); h_len = sizeof (*ip); } ip->ip_v = IPVERSION; ip->ip_hl = h_len >> 2; ip->ip_tos = socket->ip_tos; ip->ip_len = htons (tx_len); ip->ip_id = _get_ip_id(); ip->ip_off = 0; ip->ip_ttl = socket->ip_ttl; ip->ip_p = socket->so_proto; ip->ip_src.s_addr = gethostid(); ip->ip_dst.s_addr = dest; ip->ip_sum = 0; ip->ip_sum = ~checksum (ip, h_len); } DEBUG_TX (NULL, ip); if (!_eth_send(tx_len)) { SOCK_DEBUGF ((socket, ", ENETDOWN")); SOCK_ERR (ENETDOWN); return (-1); } if (buf) buf += tx_len; 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; } }