lINUX iCmp协议

ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

在Linux kernel中密切相关的函数是下面几个:

void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
int icmp_rcv(struct sk_buff *skb);
void icmp_err(struct sk_buff *skb, u32 info);
int icmp_init(void);
void icmp_out_count(struct net *net, unsigned char type);

相关结构:

static const struct net_protocol icmp_protocol = {
 .handler = icmp_rcv,
 .err_handler = icmp_err,
 .no_policy = 1,
 .netns_ok = 1,
};

 

 if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
  pr_crit("%s: Cannot add ICMP protocol\n", __func__);

 

 


/*
 * 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, __be32 info)
{
 struct iphdr *iph;
 int room;
 struct icmp_bxm *icmp_param;
 struct rtable *rt = skb_rtable(skb_in);
 struct ipcm_cookie ipc;
 struct flowi4 fl4;
 __be32 saddr;
 u8  tos;
 u32 mark;
 struct net *net;
 struct sock *sk;

 if (!rt)
  goto out;
 net = dev_net(rt->dst.dev);

 /*
  * Find the original header. It is expected to be valid, of course.
  * Check this, icmp_send is called from the most obscure devices
  * sometimes.
  */
 iph = ip_hdr(skb_in);

 if ((u8 *)iph < skb_in->head ||
     (skb_network_header(skb_in) + sizeof(*iph)) >
     skb_tail_pointer(skb_in))
  goto out;

 /*
  * No replies to physical multicast/broadcast
  */
 if (skb_in->pkt_type != PACKET_HOST)
  goto out;

 /*
  * Now check at the protocol level
  */
 if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
  goto out;

 /*
  * Only reply to fragment 0. We byte re-order the constant
  * mask for efficiency.
  */
 if (iph->frag_off & htons(IP_OFFSET))
  goto out;

 /*
  * 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) {
   u8 _inner_type, *itp;

   itp = skb_header_pointer(skb_in,
       skb_network_header(skb_in) +
       (iph->ihl << 2) +
       offsetof(struct icmphdr,
         type) -
       skb_in->data,
       sizeof(_inner_type),
       &_inner_type);
   if (!itp)
    goto out;

   /*
    * Assume any unknown ICMP type is an error. This
    * isn't specified by the RFC, but think about it..
    */
   if (*itp > NR_ICMP_TYPES ||
       icmp_pointers[*itp].error)
    goto out;
  }
 }

 icmp_param = kmalloc(sizeof(*icmp_param), GFP_ATOMIC);
 if (!icmp_param)
  return;

 sk = icmp_xmit_lock(net);
 if (!sk)
  goto out_free;

 /*
  * Construct source address and options.
  */

 saddr = iph->daddr;
 if (!(rt->rt_flags & RTCF_LOCAL)) {
  struct net_device *dev = NULL;

  rcu_read_lock();
  if (rt_is_input_route(rt) &&
      net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr)
   dev = dev_get_by_index_rcu(net, inet_iif(skb_in));

  if (dev)
   saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
  else
   saddr = 0;
  rcu_read_unlock();
 }

 tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
        IPTOS_PREC_INTERNETCONTROL) :
       iph->tos;
 mark = IP4_REPLY_MARK(net, skb_in->mark);

 if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in))
  goto out_unlock;


 /*
  * Prepare data for ICMP header.
  */

 icmp_param->data.icmph.type  = type;
 icmp_param->data.icmph.code  = code;
 icmp_param->data.icmph.un.gateway = info;
 icmp_param->data.icmph.checksum  = 0;
 icmp_param->skb   = skb_in;
 icmp_param->offset = skb_network_offset(skb_in);
 inet_sk(sk)->tos = tos;
 sk->sk_mark = mark;
 ipc.addr = iph->saddr;
 ipc.opt = &icmp_param->replyopts.opt;
 ipc.tx_flags = 0;
 ipc.ttl = 0;
 ipc.tos = -1;

 rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark,
          type, code, icmp_param);
 if (IS_ERR(rt))
  goto out_unlock;

 if (!icmpv4_xrlim_allow(net, rt, &fl4, type, code))
  goto ende;

 /* RFC says return as much as we can without exceeding 576 bytes. */

 room = dst_mtu(&rt->dst);
 if (room > 576)
  room = 576;
 room -= sizeof(struct iphdr) + icmp_param->replyopts.opt.opt.optlen;
 room -= sizeof(struct icmphdr);

 icmp_param->data_len = skb_in->len - icmp_param->offset;
 if (icmp_param->data_len > room)
  icmp_param->data_len = room;
 icmp_param->head_len = sizeof(struct icmphdr);

 icmp_push_reply(icmp_param, &fl4, &ipc, &rt);
ende:
 ip_rt_put(rt);
out_unlock:
 icmp_xmit_unlock(sk);
out_free:
 kfree(icmp_param);
out:;
}


/*
 * Deal with incoming ICMP packets.
 */
int icmp_rcv(struct sk_buff *skb)
{
 struct icmphdr *icmph;
 struct rtable *rt = skb_rtable(skb);
 struct net *net = dev_net(rt->dst.dev);
 bool success;

 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
  struct sec_path *sp = skb_sec_path(skb);
  int nh;

  if (!(sp && sp->xvec[sp->len - 1]->props.flags &
     XFRM_STATE_ICMP))
   goto drop;

  if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
   goto drop;

  nh = skb_network_offset(skb);
  skb_set_network_header(skb, sizeof(*icmph));

  if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
   goto drop;

  skb_set_network_header(skb, nh);
 }

 __ICMP_INC_STATS(net, ICMP_MIB_INMSGS);

 if (skb_checksum_simple_validate(skb))
  goto csum_error;

 if (!pskb_pull(skb, sizeof(*icmph)))
  goto error;

 icmph = icmp_hdr(skb);

 ICMPMSGIN_INC_STATS(net, icmph->type);
 /*
  * 18 is the highest 'known' ICMP type. Anything else is a mystery
  *
  * RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently
  *    discarded.
  */
 if (icmph->type > NR_ICMP_TYPES)
  goto error;


 /*
  * Parse the ICMP message
  */

 if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
  /*
   * RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
   *   silently ignored (we let user decide with a sysctl).
   * RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently
   *   discarded if to broadcast/multicast.
   */
  if ((icmph->type == ICMP_ECHO ||
       icmph->type == ICMP_TIMESTAMP) &&
      net->ipv4.sysctl_icmp_echo_ignore_broadcasts) {
   goto error;
  }
  if (icmph->type != ICMP_ECHO &&
      icmph->type != ICMP_TIMESTAMP &&
      icmph->type != ICMP_ADDRESS &&
      icmph->type != ICMP_ADDRESSREPLY) {
   goto error;
  }
 }

 success = icmp_pointers[icmph->type].handler(skb);

 if (success)  {
  consume_skb(skb);
  return 0;
 }

drop:
 kfree_skb(skb);
 return 0;
csum_error:
 __ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS);
error:
 __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
 goto drop;
}

#define ICMP_ECHOREPLY  0 /* Echo Reply   */
#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
#define ICMP_SOURCE_QUENCH 4 /* Source Quench  */
#define ICMP_REDIRECT  5 /* Redirect (change route) */
#define ICMP_ECHO  8 /* Echo Request   */
#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded  */
#define ICMP_PARAMETERPROB 12 /* Parameter Problem  */
#define ICMP_TIMESTAMP  13 /* Timestamp Request  */
#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply  */
#define ICMP_INFO_REQUEST 15 /* Information Request  */
#define ICMP_INFO_REPLY  16 /* Information Reply  */
#define ICMP_ADDRESS  17 /* Address Mask Request  */
#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply  */
#define NR_ICMP_TYPES  18

 


/*
 * This table is the definition of how we handle ICMP.
 */
static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {
 [ICMP_ECHOREPLY] = {
  .handler = ping_rcv,
 },
 [1] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [2] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [ICMP_DEST_UNREACH] = {
  .handler = icmp_unreach,
  .error = 1,
 },
 [ICMP_SOURCE_QUENCH] = {
  .handler = icmp_unreach,
  .error = 1,
 },
 [ICMP_REDIRECT] = {
  .handler = icmp_redirect,
  .error = 1,
 },
 [6] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [7] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [ICMP_ECHO] = {
  .handler = icmp_echo,
 },
 [9] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [10] = {
  .handler = icmp_discard,
  .error = 1,
 },
 [ICMP_TIME_EXCEEDED] = {
  .handler = icmp_unreach,
  .error = 1,
 },
 [ICMP_PARAMETERPROB] = {
  .handler = icmp_unreach,
  .error = 1,
 },
 [ICMP_TIMESTAMP] = {
  .handler = icmp_timestamp,
 },
 [ICMP_TIMESTAMPREPLY] = {
  .handler = icmp_discard,
 },
 [ICMP_INFO_REQUEST] = {
  .handler = icmp_discard,
 },
 [ICMP_INFO_REPLY] = {
  .handler = icmp_discard,
 },
 [ICMP_ADDRESS] = {
  .handler = icmp_discard,
 },
 [ICMP_ADDRESSREPLY] = {
  .handler = icmp_discard,
 },
};

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值