Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: ICMPV6: add response to ICMPV6 RFC 8335 PROBE messages

This patch builds off of commit 2b246b2569cd2ac6ff700d0dce56b8bae29b1842
and adds functionality to respond to ICMPV6 PROBE requests.

Add icmp_build_probe function to construct PROBE requests for both
ICMPV4 and ICMPV6.

Modify icmpv6_rcv to detect ICMPV6 PROBE messages and call the
icmpv6_echo_reply handler.

Modify icmpv6_echo_reply to build a PROBE response message based on the
queried interface.

This patch has been tested using a branch of the iputils git repo which can
be found here: https://github.com/Juniper-Clinic-2020/iputils/tree/probe-request

Signed-off-by: Andreas Roeseler <andreas.a.roeseler@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Andreas Roeseler and committed by
David S. Miller
1fd07f33 83300c69

+60 -25
+1
include/net/icmp.h
··· 57 57 int icmp_err(struct sk_buff *skb, u32 info); 58 58 int icmp_init(void); 59 59 void icmp_out_count(struct net *net, unsigned char type); 60 + bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr); 60 61 61 62 #endif /* _ICMP_H */
+41 -22
net/ipv4/icmp.c
··· 993 993 994 994 static bool icmp_echo(struct sk_buff *skb) 995 995 { 996 - struct icmp_ext_hdr *ext_hdr, _ext_hdr; 997 - struct icmp_ext_echo_iio *iio, _iio; 998 996 struct icmp_bxm icmp_param; 999 - struct net_device *dev; 1000 - char buff[IFNAMSIZ]; 1001 997 struct net *net; 1002 - u16 ident_len; 1003 - u8 status; 1004 998 1005 999 net = dev_net(skb_dst(skb)->dev); 1006 1000 /* should there be an ICMP stat for ignored echos? */ ··· 1007 1013 icmp_param.data_len = skb->len; 1008 1014 icmp_param.head_len = sizeof(struct icmphdr); 1009 1015 1010 - if (icmp_param.data.icmph.type == ICMP_ECHO) { 1016 + if (icmp_param.data.icmph.type == ICMP_ECHO) 1011 1017 icmp_param.data.icmph.type = ICMP_ECHOREPLY; 1012 - goto send_reply; 1013 - } 1014 - if (!net->ipv4.sysctl_icmp_echo_enable_probe) 1018 + else if (!icmp_build_probe(skb, &icmp_param.data.icmph)) 1015 1019 return true; 1020 + 1021 + icmp_reply(&icmp_param, skb); 1022 + return true; 1023 + } 1024 + 1025 + /* Helper for icmp_echo and icmpv6_echo_reply. 1026 + * Searches for net_device that matches PROBE interface identifier 1027 + * and builds PROBE reply message in icmphdr. 1028 + * 1029 + * Returns false if PROBE responses are disabled via sysctl 1030 + */ 1031 + 1032 + bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr) 1033 + { 1034 + struct icmp_ext_hdr *ext_hdr, _ext_hdr; 1035 + struct icmp_ext_echo_iio *iio, _iio; 1036 + struct net *net = dev_net(skb->dev); 1037 + struct net_device *dev; 1038 + char buff[IFNAMSIZ]; 1039 + u16 ident_len; 1040 + u8 status; 1041 + 1042 + if (!net->ipv4.sysctl_icmp_echo_enable_probe) 1043 + return false; 1044 + 1016 1045 /* We currently only support probing interfaces on the proxy node 1017 1046 * Check to ensure L-bit is set 1018 1047 */ 1019 - if (!(ntohs(icmp_param.data.icmph.un.echo.sequence) & 1)) 1020 - return true; 1048 + if (!(ntohs(icmphdr->un.echo.sequence) & 1)) 1049 + return false; 1021 1050 /* Clear status bits in reply message */ 1022 - icmp_param.data.icmph.un.echo.sequence &= htons(0xFF00); 1023 - icmp_param.data.icmph.type = ICMP_EXT_ECHOREPLY; 1051 + icmphdr->un.echo.sequence &= htons(0xFF00); 1052 + if (icmphdr->type == ICMP_EXT_ECHO) 1053 + icmphdr->type = ICMP_EXT_ECHOREPLY; 1054 + else 1055 + icmphdr->type = ICMPV6_EXT_ECHO_REPLY; 1024 1056 ext_hdr = skb_header_pointer(skb, 0, sizeof(_ext_hdr), &_ext_hdr); 1025 1057 /* Size of iio is class_type dependent. 1026 1058 * Only check header here and assign length based on ctype in the switch statement ··· 1107 1087 goto send_mal_query; 1108 1088 } 1109 1089 if (!dev) { 1110 - icmp_param.data.icmph.code = ICMP_EXT_CODE_NO_IF; 1111 - goto send_reply; 1090 + icmphdr->code = ICMP_EXT_CODE_NO_IF; 1091 + return true; 1112 1092 } 1113 1093 /* Fill bits in reply message */ 1114 1094 if (dev->flags & IFF_UP) ··· 1118 1098 if (!list_empty(&rcu_dereference(dev->ip6_ptr)->addr_list)) 1119 1099 status |= ICMP_EXT_ECHOREPLY_IPV6; 1120 1100 dev_put(dev); 1121 - icmp_param.data.icmph.un.echo.sequence |= htons(status); 1122 - send_reply: 1123 - icmp_reply(&icmp_param, skb); 1124 - return true; 1101 + icmphdr->un.echo.sequence |= htons(status); 1102 + return true; 1125 1103 send_mal_query: 1126 - icmp_param.data.icmph.code = ICMP_EXT_CODE_MAL_QUERY; 1127 - goto send_reply; 1104 + icmphdr->code = ICMP_EXT_CODE_MAL_QUERY; 1105 + return true; 1128 1106 } 1107 + EXPORT_SYMBOL_GPL(icmp_build_probe); 1129 1108 1130 1109 /* 1131 1110 * Handle ICMP Timestamp requests.
+18 -3
net/ipv6/icmp.c
··· 725 725 struct ipcm6_cookie ipc6; 726 726 u32 mark = IP6_REPLY_MARK(net, skb->mark); 727 727 bool acast; 728 + u8 type; 728 729 729 730 if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) && 730 731 net->ipv6.sysctl.icmpv6_echo_ignore_multicast) ··· 741 740 !(net->ipv6.sysctl.anycast_src_echo_reply && acast)) 742 741 saddr = NULL; 743 742 743 + if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST) 744 + type = ICMPV6_EXT_ECHO_REPLY; 745 + else 746 + type = ICMPV6_ECHO_REPLY; 747 + 744 748 memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); 745 - tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY; 749 + tmp_hdr.icmp6_type = type; 746 750 747 751 memset(&fl6, 0, sizeof(fl6)); 748 752 if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES) ··· 758 752 if (saddr) 759 753 fl6.saddr = *saddr; 760 754 fl6.flowi6_oif = icmp6_iif(skb); 761 - fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; 755 + fl6.fl6_icmp_type = type; 762 756 fl6.flowi6_mark = mark; 763 757 fl6.flowi6_uid = sock_net_uid(net, NULL); 764 758 security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6)); ··· 789 783 790 784 msg.skb = skb; 791 785 msg.offset = 0; 792 - msg.type = ICMPV6_ECHO_REPLY; 786 + msg.type = type; 793 787 794 788 ipcm6_init_sk(&ipc6, np); 795 789 ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 796 790 ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb)); 797 791 ipc6.sockc.mark = mark; 792 + 793 + if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST) 794 + if (!icmp_build_probe(skb, (struct icmphdr *)&tmp_hdr)) 795 + goto out_dst_release; 798 796 799 797 if (ip6_append_data(sk, icmpv6_getfrag, &msg, 800 798 skb->len + sizeof(struct icmp6hdr), ··· 919 909 switch (type) { 920 910 case ICMPV6_ECHO_REQUEST: 921 911 if (!net->ipv6.sysctl.icmpv6_echo_ignore_all) 912 + icmpv6_echo_reply(skb); 913 + break; 914 + case ICMPV6_EXT_ECHO_REQUEST: 915 + if (!net->ipv6.sysctl.icmpv6_echo_ignore_all && 916 + net->ipv4.sysctl_icmp_echo_enable_probe) 922 917 icmpv6_echo_reply(skb); 923 918 break; 924 919