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); }
/** * 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)); }
/** * 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); }
/* * 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)); }
/* * 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; } }
/** * Enqueue a link-layer frame (IPv4/v6 only) to the internal loopback device. * * \note This function uses call-by-value. Thus `pkt' buffer can * be modified by loopback_device() and loopback handler may * send using _eth_send(). * * \note Loopback device cannot send to itself (potential recursion). */ static int send_loopback (link_Packet pkt, BOOL is_ip6, unsigned *err_line) { struct pkt_ringbuf *q; const in_Header *ip; int ip_len; SIO_TRACE (("send_loopback")); if (!_pkt_inf) { *err_line = __LINE__; goto drop_it; } /* Call loopback handler with IP-packet */ ip = (in_Header*) ((BYTE*)&pkt + _pkt_ip_ofs); ip_len = loopback_device ((in_Header*)ip); q = &_pkt_inf->pkt_queue; if (!q || ip_len > (int)_mtu) { *err_line = __LINE__; goto drop_it; } if (ip_len > 0) { #if defined(USE_FAST_PKT) /* * Don't let pkt_receiver() modify the queue while testing/copying. */ if (pkt_buffers_used() >= _pkt_inf->pkt_queue.num_buf - 1) { *err_line = __LINE__; goto drop_it; } { char tx_buf [ETH_MAX]; unsigned len = ip_len; /* Enqueue packet to head of input IP-queue. */ if (!_pktserial) { void *data = (*mac_tx_format) (tx_buf, _eth_addr, is_ip6 ? IP6_TYPE : IP4_TYPE); memcpy (MAC_SRC(data), &_eth_loop_addr, sizeof(mac_address)); memcpy (data, ip, ip_len); len += _pkt_ip_ofs; } else memcpy (tx_buf, ip, ip_len); if (!pkt_append_recv(tx_buf, len)) { *err_line = __LINE__; goto drop_it; } } #elif defined(WIN32) struct pkt_rx_element *head; ENTER_CRIT(); if (pktq_in_index(q) == q->out_index) /* queue is full, drop it */ { q->num_drop++; LEAVE_CRIT(); *err_line = __LINE__; goto drop_it; } head = (struct pkt_rx_element*) pktq_in_buf (q); head->rx_length = _pkt_ip_ofs + ip_len; gettimeofday (&head->tstamp_put, NULL); /* Enqueue packet to head of input IP-queue. */ if (!_pktserial) { void *data = (*mac_tx_format) (&head->rx_buf, _eth_addr, is_ip6 ? IP6_TYPE : IP4_TYPE); memcpy (MAC_SRC(data), &_eth_loop_addr, sizeof(mac_address)); memcpy (data, ip, ip_len); } else memcpy (head, ip, ip_len); /* Update queue head index */ q->in_index = pktq_in_index (q); LEAVE_CRIT(); #else union link_Packet *head; DISABLE(); if (pktq_in_index(q) == q->out_index) /* queue is full, drop it */ { q->num_drop++; ENABLE(); *err_line = __LINE__; goto drop_it; } head = (union link_Packet*) pktq_in_buf (q); /* Enqueue packet to head of input IP-queue. */ if (!_pktserial) { void *data = (*mac_tx_format) (head, _eth_addr, is_ip6 ? IP6_TYPE : IP4_TYPE); memcpy (MAC_SRC(data), &_eth_loop_addr, sizeof(mac_address)); memcpy (data, ip, ip_len); } else memcpy (head, ip, ip_len); /* Update queue head index */ q->in_index = pktq_in_index (q); ENABLE(); #endif } *err_line = 0; return (ip_len + _pkt_ip_ofs); drop_it: /* * Maybe this should be an input counter */ if (is_ip6) STAT (ip6stats.ip6s_odropped++); else STAT (ip4stats.ips_odropped++); return (0); }