首页 > 系统相关 >lINUX iCmp协议

lINUX iCmp协议

时间:2023-06-01 10:37:06浏览次数:55  
标签:协议 iCmp struct ICMP lINUX error skb icmp type


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,
  },
 };

标签:协议,iCmp,struct,ICMP,lINUX,error,skb,icmp,type
From: https://blog.51cto.com/u_11860992/6392693

相关文章

  • 网络层协议注册
    staticDEFINE_SPINLOCK(ptype_lock);structlist_headptype_base[PTYPE_HASH_SIZE];structlist_headptype_all;/*Taps*/structpacket_type{__be16type;/*Thisisreallyhtons(ether_type).*/structnet_device*dev;/*NULLiswildcardedhere......
  • Linux创建socket
    staticconststructnet_proto_familyinet_family_ops={.family=PF_INET,.create=inet_create,.owner=THIS_MODULE,};/**Createaninetsocket.*/staticintinet_create(structnet*net,structsocket*sock,intprotocol,intkern)......
  • Linux 内核 net_proto_family
    staticconststructnet_proto_familyinet_family_ops={.family=PF_INET,.create=inet_create,.owner=THIS_MODULE,};(void)sock_register(&inet_family_ops);/***sock_register-addasocketprotocolhandler*@ops:descriptiono......
  • 发送IP封包到高层协议
    intip_local_deliver(structsk_buff*skb){/**ReassembleIPfragments.*/structnet*net=dev_net(skb->dev);if(ip_is_fragment(ip_hdr(skb))){if(ip_defrag(net,skb,IP_DEFRAG_LOCAL_DELIVER))return0;}returnNF_HOOK......
  • Linux 内核时钟架构之时钟源读取计数
    前面我们讲到,时钟源是给timekeeping使用的,timekeeping会定时更新,这就依赖timekeeping模块需要读取clocksource的计数,计算时间流逝。然后对时间进行叠加,得到当前时间。 ktime_get()--->tk_core.timekeeperclocksource.read()timekeeping_get_ns()--》read()......
  • linux quota命令使用——应用场景 针对不同的用户设置不同的磁盘访问大小
    quota显示磁盘已使用的空间与限制Linuxquota命令语法quota[选项][用户|组群]命令中各选项的含义如表所示。  Linuxquota命令示例显示用户zhangsan的磁盘使用情况和限制[root@rhel~]#su-zhangsan//以用户zhangsan登录系统[zhangsan@rhel~]$quotaDiskquotasforuse......
  • Linux 内核时钟架构之时钟事件设备与tick_device
    每个CPU定义了一个tick_device,其用于对本cpu使用的时钟事件设备跟踪。也就是说,tick_device是有的,但是这里面有没有clock_event_device我们并不清楚,但是内核在启动时候,如果注册clock_event_device设备,那么内核尝试用时钟事件设备与tick_device设备绑定。这样,两则就关联起来了。......
  • Linux 内核时钟架构之时钟事件设备注册
    voidclockevents_register_device(structclock_event_device*dev);voidclockevents_config_and_register(structclock_event_device*dev,u32freq,unsignedlongmin_delta,unsignedlongmax_delta);相关的一个是配置函数voidclocke......
  • Linux 内核时钟之timer初始化
    init_timersvoid__initinit_timers(void){init_timer_cpus();init_timer_stats();open_softirq(TIMER_SOFTIRQ,run_timer_softirq);}staticvoid__initinit_timer_cpu(intcpu){structtimer_base*base;inti;for(i=0;i<NR_BASES;i+......
  • Linux 添加redis守护进程
    1、编写启动服务文件 /lib/systemd/system/redis.service[Unit]Description=Theredis-serverProcessManagerDocumentation=https://redis.io/After=network.target[Service]Type=forking#根据自己的redis路径启动和停止ExecStart=/usr/local/redis/bin/redis-server/usr/l......