📄 icmp.c
字号:
ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);out_put: if (likely(idev != NULL)) in6_dev_put(idev);out_dst_release: dst_release(dst);out: icmpv6_xmit_unlock();}static void icmpv6_echo_reply(struct sk_buff *skb){ struct sock *sk = icmpv6_socket->sk; struct inet6_dev *idev; struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *saddr = NULL; struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct icmp6hdr tmp_hdr; struct flowi fl; struct icmpv6_msg msg; struct dst_entry *dst; int err = 0; int hlimit = -1; saddr = &skb->nh.ipv6h->daddr; if (!ipv6_unicast_destination(skb)) saddr = NULL; memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY; memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_ICMPV6; ipv6_addr_copy(&fl.fl6_dst, &skb->nh.ipv6h->saddr); if (saddr) ipv6_addr_copy(&fl.fl6_src, saddr); fl.oif = skb->dev->ifindex; fl.fl_icmp_type = ICMPV6_ECHO_REPLY; if (icmpv6_xmit_lock()) return; if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) fl.oif = np->mcast_oif; err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) goto out_dst_release; if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; else hlimit = np->hop_limit; if (hlimit < 0) hlimit = dst_metric(dst, RTAX_HOPLIMIT); } idev = in6_dev_get(skb->dev); msg.skb = skb; msg.offset = 0; err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), sizeof(struct icmp6hdr), hlimit, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT); if (err) { ip6_flush_pending_frames(sk); goto out_put; } err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTECHOREPLIES); ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);out_put: if (likely(idev != NULL)) in6_dev_put(idev);out_dst_release: dst_release(dst);out: icmpv6_xmit_unlock();}static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info){ struct in6_addr *saddr, *daddr; struct inet6_protocol *ipprot; struct sock *sk; int inner_offset; int hash; u8 nexthdr; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) return; nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr; if (ipv6_ext_hdr(nexthdr)) { /* now skip over extension headers */ inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, skb->len - sizeof(struct ipv6hdr)); if (inner_offset<0) return; } else { inner_offset = sizeof(struct ipv6hdr); } /* Checkin header including 8 bytes of inner protocol header. */ if (!pskb_may_pull(skb, inner_offset+8)) return; saddr = &skb->nh.ipv6h->saddr; daddr = &skb->nh.ipv6h->daddr; /* BUGGG_FUTURE: we should try to parse exthdrs in this packet. Without this we will not able f.e. to make source routed pmtu discovery. Corresponding argument (opt) to notifiers is already added. --ANK (980726) */ hash = nexthdr & (MAX_INET_PROTOS - 1); rcu_read_lock(); ipprot = rcu_dereference(inet6_protos[hash]); if (ipprot && ipprot->err_handler) ipprot->err_handler(skb, NULL, type, code, inner_offset, info); rcu_read_unlock(); read_lock(&raw_v6_lock); if ((sk = sk_head(&raw_v6_htable[hash])) != NULL) { while((sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr))) { rawv6_err(sk, skb, NULL, type, code, inner_offset, info); sk = sk_next(sk); } } read_unlock(&raw_v6_lock);} /* * Handle icmp messages */static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp){ struct sk_buff *skb = *pskb; struct net_device *dev = skb->dev; struct inet6_dev *idev = __in6_dev_get(dev); struct in6_addr *saddr, *daddr; struct ipv6hdr *orig_hdr; struct icmp6hdr *hdr; int type; ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); saddr = &skb->nh.ipv6h->saddr; daddr = &skb->nh.ipv6h->daddr; /* Perform checksum. */ if (skb->ip_summed == CHECKSUM_HW) { skb->ip_summed = CHECKSUM_UNNECESSARY; if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, skb->csum)) { LIMIT_NETDEBUG( printk(KERN_DEBUG "ICMPv6 hw checksum failed\n")); skb->ip_summed = CHECKSUM_NONE; } } if (skb->ip_summed == CHECKSUM_NONE) { if (csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, skb_checksum(skb, 0, skb->len, 0))) { LIMIT_NETDEBUG( printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n", NIP6(*saddr), NIP6(*daddr))); goto discard_it; } } if (!pskb_pull(skb, sizeof(struct icmp6hdr))) goto discard_it; hdr = (struct icmp6hdr *) skb->h.raw; type = hdr->icmp6_type; if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) ICMP6_INC_STATS_OFFSET_BH(idev, ICMP6_MIB_INDESTUNREACHS, type - ICMPV6_DEST_UNREACH); else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT) ICMP6_INC_STATS_OFFSET_BH(idev, ICMP6_MIB_INECHOS, type - ICMPV6_ECHO_REQUEST); switch (type) { case ICMPV6_ECHO_REQUEST: icmpv6_echo_reply(skb); break; case ICMPV6_ECHO_REPLY: /* we couldn't care less */ break; case ICMPV6_PKT_TOOBIG: /* BUGGG_FUTURE: if packet contains rthdr, we cannot update standard destination cache. Seems, only "advanced" destination cache will allow to solve this problem --ANK (980726) */ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto discard_it; hdr = (struct icmp6hdr *) skb->h.raw; orig_hdr = (struct ipv6hdr *) (hdr + 1); rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, ntohl(hdr->icmp6_mtu)); /* * Drop through to notify */ case ICMPV6_DEST_UNREACH: case ICMPV6_TIME_EXCEED: case ICMPV6_PARAMPROB: icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); break; case NDISC_ROUTER_SOLICITATION: case NDISC_ROUTER_ADVERTISEMENT: case NDISC_NEIGHBOUR_SOLICITATION: case NDISC_NEIGHBOUR_ADVERTISEMENT: case NDISC_REDIRECT: ndisc_rcv(skb); break; case ICMPV6_MGM_QUERY: igmp6_event_query(skb); break; case ICMPV6_MGM_REPORT: igmp6_event_report(skb); break; case ICMPV6_MGM_REDUCTION: case ICMPV6_NI_QUERY: case ICMPV6_NI_REPLY: case ICMPV6_MLD2_REPORT: case ICMPV6_DHAAD_REQUEST: case ICMPV6_DHAAD_REPLY: case ICMPV6_MOBILE_PREFIX_SOL: case ICMPV6_MOBILE_PREFIX_ADV: break; default: LIMIT_NETDEBUG( printk(KERN_DEBUG "icmpv6: msg of unknown type\n")); /* informational */ if (type & ICMPV6_INFOMSG_MASK) break; /* * error of unknown type. * must pass to upper level */ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); }; kfree_skb(skb); return 0;discard_it: ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); kfree_skb(skb); return 0;}int __init icmpv6_init(struct net_proto_family *ops){ struct sock *sk; int err, i, j; for (i = 0; i < NR_CPUS; i++) { if (!cpu_possible(i)) continue; err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &per_cpu(__icmpv6_socket, i)); if (err < 0) { printk(KERN_ERR "Failed to initialize the ICMP6 control socket " "(err %d).\n", err); goto fail; } sk = per_cpu(__icmpv6_socket, i)->sk; sk->sk_allocation = GFP_ATOMIC; /* Enough space for 2 64K ICMP packets, including * sk_buff struct overhead. */ sk->sk_sndbuf = (2 * ((64 * 1024) + sizeof(struct sk_buff))); sk->sk_prot->unhash(sk); } if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) { printk(KERN_ERR "Failed to register ICMP6 protocol\n"); err = -EAGAIN; goto fail; } return 0; fail: for (j = 0; j < i; j++) { if (!cpu_possible(j)) continue; sock_release(per_cpu(__icmpv6_socket, j)); } return err;}void icmpv6_cleanup(void){ int i; for (i = 0; i < NR_CPUS; i++) { if (!cpu_possible(i)) continue; sock_release(per_cpu(__icmpv6_socket, i)); } inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);}static struct icmp6_err { int err; int fatal;} tab_unreach[] = { { /* NOROUTE */ .err = ENETUNREACH, .fatal = 0, }, { /* ADM_PROHIBITED */ .err = EACCES, .fatal = 1, }, { /* Was NOT_NEIGHBOUR, now reserved */ .err = EHOSTUNREACH, .fatal = 0, }, { /* ADDR_UNREACH */ .err = EHOSTUNREACH, .fatal = 0, }, { /* PORT_UNREACH */ .err = ECONNREFUSED, .fatal = 1, },};int icmpv6_err_convert(int type, int code, int *err){ int fatal = 0; *err = EPROTO; switch (type) { case ICMPV6_DEST_UNREACH: fatal = 1; if (code <= ICMPV6_PORT_UNREACH) { *err = tab_unreach[code].err; fatal = tab_unreach[code].fatal; } break; case ICMPV6_PKT_TOOBIG: *err = EMSGSIZE; break; case ICMPV6_PARAMPROB: *err = EPROTO; fatal = 1; break; case ICMPV6_TIME_EXCEED: *err = EHOSTUNREACH; break; }; return fatal;}#ifdef CONFIG_SYSCTLctl_table ipv6_icmp_table[] = { { .ctl_name = NET_IPV6_ICMP_RATELIMIT, .procname = "ratelimit", .data = &sysctl_icmpv6_time, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec }, { .ctl_name = 0 },};#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -