📄 icmp.c
字号:
* Shared between ICMPv4 and ICMPv6. */#define XRLIM_BURST_FACTOR 6int xrlim_allow(struct dst_entry *dst, int timeout){ unsigned long now; now = jiffies; dst->rate_tokens += now - dst->rate_last; dst->rate_last = now; if (dst->rate_tokens > XRLIM_BURST_FACTOR*timeout) dst->rate_tokens = XRLIM_BURST_FACTOR*timeout; if (dst->rate_tokens >= timeout) { dst->rate_tokens -= timeout; return 1; } return 0; }static inline int icmpv4_xrlim_allow(struct rtable *rt, int type, int code){ struct dst_entry *dst = &rt->u.dst; if (type > NR_ICMP_TYPES || !icmp_pointers[type].timeout) return 1; /* Don't limit PMTU discovery. */ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) return 1; /* Redirect has its own rate limit mechanism */ if (type == ICMP_REDIRECT) return 1; /* No rate limit on loopback */ if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) return 1; return xrlim_allow(dst, *(icmp_pointers[type].timeout));}/* * Maintain the counters used in the SNMP statistics for outgoing ICMP */ static void icmp_out_count(int type){ if (type>NR_ICMP_TYPES) return; (*icmp_pointers[type].output)++; icmp_statistics.IcmpOutMsgs++;} /* * Checksum each fragment, and on the first include the headers and final checksum. */ static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen){ struct icmp_bxm *icmp_param = (struct icmp_bxm *)p; struct icmphdr *icmph; unsigned long csum; if (offset) { icmp_param->csum=csum_partial_copy(icmp_param->data_ptr+offset-sizeof(struct icmphdr), to, fraglen,icmp_param->csum); return 0; } /* * First fragment includes header. Note that we've done * the other fragments first, so that we get the checksum * for the whole packet here. */ csum = csum_partial_copy((void *)&icmp_param->icmph, to, sizeof(struct icmphdr), icmp_param->csum); csum = csum_partial_copy(icmp_param->data_ptr, to+sizeof(struct icmphdr), fraglen-sizeof(struct icmphdr), csum); icmph=(struct icmphdr *)to; icmph->checksum = csum_fold(csum); return 0;} /* * Driving logic for building and sending ICMP messages. */static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb){ struct sock *sk=icmp_socket->sk; struct ipcm_cookie ipc; struct rtable *rt = (struct rtable*)skb->dst; u32 daddr; if (ip_options_echo(&icmp_param->replyopts, skb)) return; icmp_param->icmph.checksum=0; icmp_param->csum=0; icmp_out_count(icmp_param->icmph.type); sk->ip_tos = skb->nh.iph->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = &icmp_param->replyopts; if (ipc.opt->srr) daddr = icmp_param->replyopts.faddr; if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0)) return; ip_build_xmit(sk, icmp_glue_bits, icmp_param, icmp_param->data_len+sizeof(struct icmphdr), &ipc, rt, MSG_DONTWAIT); ip_rt_put(rt);}/* * Send an ICMP message in response to a situation * * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do). * MUST NOT change this header information. * MUST NOT reply to a multicast/broadcast IP address. * MUST NOT reply to a multicast/broadcast MAC address. * MUST reply to only the first fragment. */void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info){ struct iphdr *iph; struct icmphdr *icmph; int room; struct icmp_bxm icmp_param; struct rtable *rt = (struct rtable*)skb_in->dst; struct ipcm_cookie ipc; u32 saddr; u8 tos; /* * Find the original header */ iph = skb_in->nh.iph; /* * No replies to physical multicast/broadcast */ if (skb_in->pkt_type!=PACKET_HOST) return; /* * Now check at the protocol level */ if (!rt) { if (sysctl_ip_always_defrag == 0 && net_ratelimit()) printk(KERN_DEBUG "icmp_send: destinationless packet\n"); return; } if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) return; /* * Only reply to fragment 0. We byte re-order the constant * mask for efficiency. */ if (iph->frag_off&htons(IP_OFFSET)) return; /* * If we send an ICMP error to an ICMP error a mess would result.. */ if (icmp_pointers[type].error) { /* * We are an error, check if we are replying to an ICMP error */ if (iph->protocol==IPPROTO_ICMP) { icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); /* * Assume any unknown ICMP type is an error. This isn't * specified by the RFC, but think about it.. */ if (icmph->type>NR_ICMP_TYPES || icmp_pointers[icmph->type].error) return; } } /* * Construct source address and options. */#ifdef CONFIG_IP_ROUTE_NAT /* * Restore original addresses if packet has been translated. */ if (rt->rt_flags&RTCF_NAT && IPCB(skb_in)->flags&IPSKB_TRANSLATED) { iph->daddr = rt->key.dst; iph->saddr = rt->key.src; }#endif#ifdef CONFIG_IP_MASQUERADE if (type==ICMP_DEST_UNREACH && IPCB(skb_in)->flags&IPSKB_MASQUERADED) { ip_fw_unmasq_icmp(skb_in); }#endif saddr = iph->daddr; if (!(rt->rt_flags & RTCF_LOCAL)) saddr = 0; tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) : iph->tos; /* XXX: use a more aggressive expire for routes created by * this call (not longer than the rate limit timeout). * It could be also worthwhile to not put them into ipv4 * fast routing cache at first. Otherwise an attacker can * grow the routing table. */ if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0)) return; if (ip_options_echo(&icmp_param.replyopts, skb_in)) goto ende; /* * Prepare data for ICMP header. */ icmp_param.icmph.type=type; icmp_param.icmph.code=code; icmp_param.icmph.un.gateway = info; icmp_param.icmph.checksum=0; icmp_param.csum=0; icmp_param.data_ptr=iph; icmp_out_count(icmp_param.icmph.type); icmp_socket->sk->ip_tos = tos; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts; if (icmp_param.replyopts.srr) { ip_rt_put(rt); if (ip_route_output(&rt, icmp_param.replyopts.faddr, saddr, RT_TOS(tos), 0)) return; } if (!icmpv4_xrlim_allow(rt, type, code)) goto ende; /* RFC says return as much as we can without exceeding 576 bytes. */ room = rt->u.dst.pmtu; if (room > 576) room = 576; room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen; room -= sizeof(struct icmphdr); icmp_param.data_len=(iph->ihl<<2)+skb_in->len; if (icmp_param.data_len > room) icmp_param.data_len = room; ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param, icmp_param.data_len+sizeof(struct icmphdr), &ipc, rt, MSG_DONTWAIT);ende: ip_rt_put(rt);}/* * Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH. */static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len){ struct iphdr *iph; int hash; struct inet_protocol *ipprot; unsigned char *dp; struct sock *raw_sk; /* * Incomplete header ? * Only checks for the IP header, there should be an * additional check for longer headers in upper levels. */ if(len<sizeof(struct iphdr)) { icmp_statistics.IcmpInErrors++; return; } iph = (struct iphdr *) (icmph + 1); dp = (unsigned char*)iph; if(icmph->type==ICMP_DEST_UNREACH) { switch(icmph->code & 15) { case ICMP_NET_UNREACH: break; case ICMP_HOST_UNREACH: break; case ICMP_PROT_UNREACH: break; case ICMP_PORT_UNREACH: break; case ICMP_FRAG_NEEDED: if (ipv4_config.no_pmtu_disc) { if (sysctl_ip_always_defrag == 0 && net_ratelimit()) printk(KERN_INFO "ICMP: %d.%d.%d.%d: fragmentation needed and DF set.\n", NIPQUAD(iph->daddr)); } else { unsigned short new_mtu; new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu)); if (!new_mtu) return; icmph->un.frag.mtu = htons(new_mtu); } break; case ICMP_SR_FAILED: if (sysctl_ip_always_defrag == 0 && net_ratelimit()) printk(KERN_INFO "ICMP: %d.%d.%d.%d: Source Route Failed.\n", NIPQUAD(iph->daddr)); break; default: break; } if (icmph->code>NR_ICMP_UNREACH) return; } /* * Throw it at our lower layers * * RFC 1122: 3.2.2 MUST extract the protocol ID from the passed header. * RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer. * RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer. */ /* * Check the other end isnt violating RFC 1122. Some routers send * bogus responses to broadcast frames. If you see this message * first check your netmask matches at both ends, if it does then * get the other vendor to fix their kit. */ if (!sysctl_icmp_ignore_bogus_error_responses) { if (inet_addr_type(iph->daddr) == RTN_BROADCAST) { if (net_ratelimit()) printk(KERN_WARNING "%d.%d.%d.%d sent an invalid ICMP error to a broadcast.\n", NIPQUAD(skb->nh.iph->saddr)); return; } } /* * Deliver ICMP message to raw sockets. Pretty useless feature? */ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ hash = iph->protocol & (MAX_INET_PROTOS - 1); if ((raw_sk = raw_v4_htable[hash]) != NULL) { while ((raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr, skb->dev->ifindex)) != NULL) { raw_err(raw_sk, skb); raw_sk = raw_sk->next; } } /* * This can't change while we are doing it. */ ipprot = (struct inet_protocol *) inet_protos[hash]; while(ipprot != NULL) { struct inet_protocol *nextip;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -