Index: share/man/man4/icmp6.4 =================================================================== --- share/man/man4/icmp6.4 +++ share/man/man4/icmp6.4 @@ -238,6 +238,29 @@ with a pointer to the .Vt icmp6_filter structure as the option value. +.Ss MIB Variables +The +.Tn ICMPv6 +protocol implements a number of variables in the +.Va net.inet6.icmp6 +branch of the +.Xr sysctl 3 +MIB. +.Bl -tag -width "icmplim_output" +.It Va reply_src +.Pq Vt str +An interface name used for the ICMPv6 reply source in response to packets +which are not directly addressed to us. +By default continue with normal source selection. +.It Va reply_from_interface +.Pq Vt boolean +Use the IP address of the interface the packet came through in for +responses to packets which are not directly addressed to us. +If enabled, this rule is processed before all others. +By default, continue with normal source selection. +Enabling this option is particularly useful on routers because it +makes external traceroutes show the actual path a packet has taken +instead of the possibly different return path. .Sh SEE ALSO .Xr getsockopt 2 , .Xr recv 2 , Index: sys/netinet/icmp6.h =================================================================== --- sys/netinet/icmp6.h +++ sys/netinet/icmp6.h @@ -692,6 +692,8 @@ #define ICMPV6CTL_MAXID 26 #ifdef _KERNEL +#include + # ifdef __STDC__ struct nhop_object; struct rttimer; @@ -782,6 +784,9 @@ #define ICMP6_NODEINFO_NODEADDROK 0x2 #define ICMP6_NODEINFO_TMPADDROK 0x4 #define ICMP6_NODEINFO_GLOBALOK 0x8 + +SYSCTL_DECL(_net_inet6_icmp6); + #endif /* _KERNEL */ #endif /* not _NETINET_ICMP6_H_ */ Index: sys/netinet6/icmp6.c =================================================================== --- sys/netinet6/icmp6.c +++ sys/netinet6/icmp6.c @@ -129,6 +129,8 @@ VNET_DEFINE_STATIC(int, icmp6errpps_count) = 0; VNET_DEFINE_STATIC(struct timeval, icmp6errppslim_last); VNET_DECLARE(int, icmp6_nodeinfo); +VNET_DEFINE_STATIC(int, icmp6_replyif) = 0; +VNET_DEFINE_STATIC(char, reply_src6[IFNAMSIZ]); #define V_ripcbinfo VNET(ripcbinfo) #define V_ripcb VNET(ripcb) @@ -136,6 +138,8 @@ #define V_icmp6errpps_count VNET(icmp6errpps_count) #define V_icmp6errppslim_last VNET(icmp6errppslim_last) #define V_icmp6_nodeinfo VNET(icmp6_nodeinfo) +#define V_icmp6_replyif VNET(icmp6_replyif) +#define V_reply_src6 VNET(reply_src6) static void icmp6_errcount(int, int); static int icmp6_rip6_input(struct mbuf **, int); @@ -152,6 +156,14 @@ struct ifnet *, int); static int icmp6_notify_error(struct mbuf **, int, int, int); +SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, reply_from_interface, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(icmp6_replyif), 0, + "ICMP6 reply from incoming interface for non-local packets"); + +SYSCTL_STRING(_net_inet6_icmp6, OID_AUTO, reply_src, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(reply_src6), IFNAMSIZ, + "ICMP6 reply source for non-local packets"); + /* * Kernel module interface for updating icmp6stat. The argument is an index * into icmp6stat treated as an array of u_quad_t. While this encodes the @@ -2125,26 +2137,69 @@ int error; struct in6_addr dst6; uint32_t scopeid; + struct ifnet *ifp; + struct ifaddr *ifa; + struct in6_ifaddr *ifa6; /* * This case matches to multicasts, our anycast, or unicasts * that we do not own. Select a source address based on the * source address of the erroneous packet. */ + ifp = m->m_pkthdr.rcvif; in6_splitscope(&ip6->ip6_src, &dst6, &scopeid); - error = in6_selectsrc_addr(M_GETFIB(m), &dst6, - scopeid, NULL, &src6, &hlim); - if (error) { - char ip6buf[INET6_ADDRSTRLEN]; - nd6log((LOG_DEBUG, - "icmp6_reflect: source can't be determined: " - "dst=%s, error=%d\n", - ip6_sprintf(ip6buf, &ip6->ip6_dst), error)); - goto bad; + if (V_icmp6_replyif && ifp != NULL) { + hlim = in6_selecthlim(NULL, ifp); + CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + ifa6 = (struct in6_ifaddr *)ifa; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (in6_getscope(&ifa6->ia_addr.sin6_addr) != scopeid) + continue; + ia = ifatoia6(ifa); + srcp = IA6_IN6(ia); + break; + } + } else if (V_reply_src6[0] != '\0' && (ifp = ifunit(V_reply_src6))) { + hlim = in6_selecthlim(NULL, ifp); + CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + ifa6 = (struct in6_ifaddr *)ifa; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (in6_getscope(&ifa6->ia_addr.sin6_addr) != scopeid) + continue; + ia = ifatoia6(ifa); + srcp = IA6_IN6(ia); + break; + } + } else { + error = in6_selectsrc_addr(M_GETFIB(m), &dst6, + scopeid, NULL, &src6, &hlim); + + if (error) { + char ip6buf[INET6_ADDRSTRLEN]; + nd6log((LOG_DEBUG, + "icmp6_reflect: source can't be determined: " + "dst=%s, error=%d\n", + ip6_sprintf(ip6buf, &ip6->ip6_dst), error)); + goto bad; + } + srcp = &src6; } - srcp = &src6; } + /* + * If address selection above did not give a source address, fail. + */ + if (srcp == NULL) { + char ip6buf[INET6_ADDRSTRLEN]; + nd6log((LOG_DEBUG, + "icmp6_reflect: source address is null: " + "dst=%s\n", + ip6_sprintf(ip6buf, &ip6->ip6_dst))); + goto bad; + } + /* * ip6_input() drops a packet if its src is multicast. * So, the src is never multicast. Index: sys/netinet6/nd6_rtr.c =================================================================== --- sys/netinet6/nd6_rtr.c +++ sys/netinet6/nd6_rtr.c @@ -103,8 +103,6 @@ VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1; #endif -SYSCTL_DECL(_net_inet6_icmp6); - /* RTPREF_MEDIUM has to be 0! */ #define RTPREF_HIGH 1 #define RTPREF_MEDIUM 0