diff options
Diffstat (limited to 'freebsd/sys/netinet/ip_icmp.c')
-rw-r--r-- | freebsd/sys/netinet/ip_icmp.c | 248 |
1 files changed, 120 insertions, 128 deletions
diff --git a/freebsd/sys/netinet/ip_icmp.c b/freebsd/sys/netinet/ip_icmp.c index cd581948..f34cc4bd 100644 --- a/freebsd/sys/netinet/ip_icmp.c +++ b/freebsd/sys/netinet/ip_icmp.c @@ -35,7 +35,6 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/local/opt_inet.h> -#include <rtems/bsd/local/opt_ipsec.h> #include <rtems/bsd/sys/param.h> #include <sys/systm.h> @@ -44,15 +43,19 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <sys/time.h> #include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/rmlock.h> #include <sys/sysctl.h> #include <sys/syslog.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_types.h> #include <net/route.h> #include <net/vnet.h> #include <netinet/in.h> +#include <netinet/in_fib.h> #include <netinet/in_pcb.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> @@ -60,16 +63,13 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip_icmp.h> #include <netinet/ip_var.h> #include <netinet/ip_options.h> +#include <netinet/sctp.h> #include <netinet/tcp.h> #include <netinet/tcp_var.h> #include <netinet/tcpip.h> #include <netinet/icmp_var.h> #ifdef INET -#ifdef IPSEC -#include <netipsec/ipsec.h> -#include <netipsec/key.h> -#endif #include <machine/in_cksum.h> @@ -83,68 +83,79 @@ __FBSDID("$FreeBSD$"); */ static VNET_DEFINE(int, icmplim) = 200; #define V_icmplim VNET(icmplim) -SYSCTL_VNET_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmplim), 0, "Maximum number of ICMP responses per second"); static VNET_DEFINE(int, icmplim_output) = 1; #define V_icmplim_output VNET(icmplim_output) -SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmplim_output), 0, - "Enable rate limiting of ICMP responses"); + "Enable logging of ICMP response rate limiting"); #ifdef INET -VNET_DEFINE(struct icmpstat, icmpstat); -SYSCTL_VNET_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RW, - &VNET_NAME(icmpstat), icmpstat, ""); +VNET_PCPUSTAT_DEFINE(struct icmpstat, icmpstat); +VNET_PCPUSTAT_SYSINIT(icmpstat); +SYSCTL_VNET_PCPUSTAT(_net_inet_icmp, ICMPCTL_STATS, stats, struct icmpstat, + icmpstat, "ICMP statistics (struct icmpstat, netinet/icmp_var.h)"); + +#ifdef VIMAGE +VNET_PCPUSTAT_SYSUNINIT(icmpstat); +#endif /* VIMAGE */ static VNET_DEFINE(int, icmpmaskrepl) = 0; #define V_icmpmaskrepl VNET(icmpmaskrepl) -SYSCTL_VNET_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmpmaskrepl), 0, - "Reply to ICMP Address Mask Request packets."); + "Reply to ICMP Address Mask Request packets"); static VNET_DEFINE(u_int, icmpmaskfake) = 0; #define V_icmpmaskfake VNET(icmpmaskfake) -SYSCTL_VNET_UINT(_net_inet_icmp, OID_AUTO, maskfake, CTLFLAG_RW, +SYSCTL_UINT(_net_inet_icmp, OID_AUTO, maskfake, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmpmaskfake), 0, - "Fake reply to ICMP Address Mask Request packets."); + "Fake reply to ICMP Address Mask Request packets"); VNET_DEFINE(int, drop_redirect) = 0; +#define V_drop_redirect VNET(drop_redirect) +SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(drop_redirect), 0, + "Ignore ICMP redirects"); static VNET_DEFINE(int, log_redirect) = 0; #define V_log_redirect VNET(log_redirect) -SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(log_redirect), 0, "Log ICMP redirects to the console"); static VNET_DEFINE(char, reply_src[IFNAMSIZ]); #define V_reply_src VNET(reply_src) -SYSCTL_VNET_STRING(_net_inet_icmp, OID_AUTO, reply_src, CTLFLAG_RW, +SYSCTL_STRING(_net_inet_icmp, OID_AUTO, reply_src, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(reply_src), IFNAMSIZ, - "icmp reply source for non-local packets."); + "ICMP reply source for non-local packets"); static VNET_DEFINE(int, icmp_rfi) = 0; #define V_icmp_rfi VNET(icmp_rfi) -SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, reply_from_interface, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, OID_AUTO, reply_from_interface, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp_rfi), 0, "ICMP reply from incoming interface for non-local packets"); static VNET_DEFINE(int, icmp_quotelen) = 8; #define V_icmp_quotelen VNET(icmp_quotelen) -SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, quotelen, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, OID_AUTO, quotelen, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp_quotelen), 0, "Number of bytes from original packet to quote in ICMP reply"); -/* - * ICMP broadcast echo sysctl - */ static VNET_DEFINE(int, icmpbmcastecho) = 0; #define V_icmpbmcastecho VNET(icmpbmcastecho) -SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW, +SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmpbmcastecho), 0, - ""); + "Reply to multicast ICMP Echo Request and Timestamp packets"); +static VNET_DEFINE(int, icmptstamprepl) = 1; +#define V_icmptstamprepl VNET(icmptstamprepl) +SYSCTL_INT(_net_inet_icmp, OID_AUTO, tstamprepl, CTLFLAG_RW, + &VNET_NAME(icmptstamprepl), 0, + "Respond to ICMP Timestamp packets"); #ifdef ICMPPRINTFS int icmpprintfs = 0; @@ -155,39 +166,6 @@ static void icmp_send(struct mbuf *, struct mbuf *); extern struct protosw inetsw[]; -static int -sysctl_net_icmp_drop_redir(SYSCTL_HANDLER_ARGS) -{ - int error, new; - int i; - struct radix_node_head *rnh; - - new = V_drop_redirect; - error = sysctl_handle_int(oidp, &new, 0, req); - if (error == 0 && req->newptr) { - new = (new != 0) ? 1 : 0; - - if (new == V_drop_redirect) - return (0); - - for (i = 0; i < rt_numfibs; i++) { - if ((rnh = rt_tables_get_rnh(i, AF_INET)) == NULL) - continue; - RADIX_NODE_HEAD_LOCK(rnh); - in_setmatchfunc(rnh, new); - RADIX_NODE_HEAD_UNLOCK(rnh); - } - - V_drop_redirect = new; - } - - return (error); -} - -SYSCTL_VNET_PROC(_net_inet_icmp, OID_AUTO, drop_redirect, - CTLTYPE_INT|CTLFLAG_RW, 0, 0, - sysctl_net_icmp_drop_redir, "I", "Ignore ICMP redirects"); - /* * Kernel module interface for updating icmpstat. The argument is an index * into icmpstat treated as an array of u_long. While this encodes the @@ -199,7 +177,7 @@ void kmod_icmpstat_inc(int statnum) { - (*((u_long *)&V_icmpstat + statnum))++; + counter_u64_add(VNET(icmpstat)[statnum], 1); } /* @@ -231,7 +209,7 @@ icmp_error(struct mbuf *n, int type, int code, uint32_t dest, int mtu) */ if (n->m_flags & M_DECRYPTED) goto freeit; - if (oip->ip_off & ~(IP_MF|IP_DF)) + if (oip->ip_off & htons(~(IP_MF|IP_DF))) goto freeit; if (n->m_flags & (M_BCAST|M_MCAST)) goto freeit; @@ -247,7 +225,7 @@ icmp_error(struct mbuf *n, int type, int code, uint32_t dest, int mtu) /* * Calculate length to quote from original packet and * prevent the ICMP mbuf from overflowing. - * Unfortunatly this is non-trivial since ip_forward() + * Unfortunately this is non-trivial since ip_forward() * sends us truncated packets. */ nlen = m_length(n, NULL); @@ -265,25 +243,54 @@ icmp_error(struct mbuf *n, int type, int code, uint32_t dest, int mtu) tcphlen = th->th_off << 2; if (tcphlen < sizeof(struct tcphdr)) goto freeit; - if (oip->ip_len < oiphlen + tcphlen) + if (ntohs(oip->ip_len) < oiphlen + tcphlen) goto freeit; if (oiphlen + tcphlen > n->m_len && n->m_next == NULL) goto stdreply; if (n->m_len < oiphlen + tcphlen && ((n = m_pullup(n, oiphlen + tcphlen)) == NULL)) goto freeit; - icmpelen = max(tcphlen, min(V_icmp_quotelen, oip->ip_len - oiphlen)); + icmpelen = max(tcphlen, min(V_icmp_quotelen, + ntohs(oip->ip_len) - oiphlen)); + } else if (oip->ip_p == IPPROTO_SCTP) { + struct sctphdr *sh; + struct sctp_chunkhdr *ch; + + if (ntohs(oip->ip_len) < oiphlen + sizeof(struct sctphdr)) + goto stdreply; + if (oiphlen + sizeof(struct sctphdr) > n->m_len && + n->m_next == NULL) + goto stdreply; + if (n->m_len < oiphlen + sizeof(struct sctphdr) && + (n = m_pullup(n, oiphlen + sizeof(struct sctphdr))) == NULL) + goto freeit; + icmpelen = max(sizeof(struct sctphdr), + min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen)); + sh = (struct sctphdr *)((caddr_t)oip + oiphlen); + if (ntohl(sh->v_tag) == 0 && + ntohs(oip->ip_len) >= oiphlen + sizeof(struct sctphdr) + 8 && + (n->m_len >= oiphlen + sizeof(struct sctphdr) + 8 || + n->m_next != NULL)) { + if (n->m_len < oiphlen + sizeof(struct sctphdr) + 8 && + (n = m_pullup(n, oiphlen + sizeof(struct sctphdr) + 8)) == NULL) + goto freeit; + ch = (struct sctp_chunkhdr *)(sh + 1); + if (ch->chunk_type == SCTP_INITIATION) { + icmpelen = max(sizeof(struct sctphdr) + 8, + min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen)); + } + } } else -stdreply: icmpelen = max(8, min(V_icmp_quotelen, oip->ip_len - oiphlen)); +stdreply: icmpelen = max(8, min(V_icmp_quotelen, ntohs(oip->ip_len) - oiphlen)); icmplen = min(oiphlen + icmpelen, nlen); if (icmplen < sizeof(struct ip)) goto freeit; if (MHLEN > sizeof(struct ip) + ICMP_MINLEN + icmplen) - m = m_gethdr(M_DONTWAIT, MT_DATA); + m = m_gethdr(M_NOWAIT, MT_DATA); else - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) goto freeit; #ifdef MAC @@ -324,8 +331,6 @@ stdreply: icmpelen = max(8, min(V_icmp_quotelen, oip->ip_len - oiphlen)); */ m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip); nip = &icp->icmp_ip; - nip->ip_len = htons(nip->ip_len); - nip->ip_off = htons(nip->ip_off); /* * Set up ICMP message mbuf and copy old IP header (without options @@ -340,7 +345,7 @@ stdreply: icmpelen = max(8, min(V_icmp_quotelen, oip->ip_len - oiphlen)); m->m_pkthdr.rcvif = n->m_pkthdr.rcvif; nip = mtod(m, struct ip *); bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip)); - nip->ip_len = m->m_len; + nip->ip_len = htons(m->m_len); nip->ip_v = IPVERSION; nip->ip_hl = 5; nip->ip_p = IPPROTO_ICMP; @@ -355,19 +360,22 @@ freeit: /* * Process a received ICMP message. */ -void -icmp_input(struct mbuf *m, int off) +int +icmp_input(struct mbuf **mp, int *offp, int proto) { struct icmp *icp; struct in_ifaddr *ia; + struct mbuf *m = *mp; struct ip *ip = mtod(m, struct ip *); struct sockaddr_in icmpsrc, icmpdst, icmpgw; - int hlen = off; - int icmplen = ip->ip_len; + int hlen = *offp; + int icmplen = ntohs(ip->ip_len) - *offp; int i, code; void (*ctlfunc)(int, struct sockaddr *, void *); int fibnum; + *mp = NULL; + /* * Locate icmp structure in mbuf, and check * that not corrupted and of at least minimum length. @@ -387,7 +395,7 @@ icmp_input(struct mbuf *m, int off) i = hlen + min(icmplen, ICMP_ADVLENMIN); if (m->m_len < i && (m = m_pullup(m, i)) == NULL) { ICMPSTAT_INC(icps_tooshort); - return; + return (IPPROTO_DONE); } ip = mtod(m, struct ip *); m->m_len -= hlen; @@ -400,19 +408,6 @@ icmp_input(struct mbuf *m, int off) m->m_len += hlen; m->m_data -= hlen; - if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* - * Deliver very specific ICMP type only. - */ - switch (icp->icmp_type) { - case ICMP_UNREACH: - case ICMP_TIMXCEED: - break; - default: - goto freeit; - } - } - #ifdef ICMPPRINTFS if (icmpprintfs) printf("icmp_input, type %d code %d\n", icp->icmp_type, @@ -489,12 +484,6 @@ icmp_input(struct mbuf *m, int off) if (code > 1) goto badcode; code = PRC_PARAMPROB; - goto deliver; - - case ICMP_SOURCEQUENCH: - if (code) - goto badcode; - code = PRC_QUENCH; deliver: /* * Problem with datagram; advise higher level routines. @@ -504,7 +493,6 @@ icmp_input(struct mbuf *m, int off) ICMPSTAT_INC(icps_badlen); goto freeit; } - icp->icmp_ip.ip_len = ntohs(icp->icmp_ip.ip_len); /* Discard ICMP's in response to multicast packets */ if (IN_MULTICAST(ntohl(icp->icmp_ip.ip_dst.s_addr))) goto badcode; @@ -517,6 +505,23 @@ icmp_input(struct mbuf *m, int off) * XXX if the packet contains [IPv4 AH TCP], we can't make a * notification to TCP layer. */ + i = sizeof(struct ip) + min(icmplen, ICMP_ADVLENPREF(icp)); + ip_stripoptions(m); + if (m->m_len < i && (m = m_pullup(m, i)) == NULL) { + /* This should actually not happen */ + ICMPSTAT_INC(icps_tooshort); + return (IPPROTO_DONE); + } + ip = mtod(m, struct ip *); + icp = (struct icmp *)(ip + 1); + /* + * The upper layer handler can rely on: + * - The outer IP header has no options. + * - The outer IP header, the ICMP header, the inner IP header, + * and the first n bytes of the inner payload are contiguous. + * n is at least 8, but might be larger based on + * ICMP_ADVLENPREF. See its definition in ip_icmp.h. + */ ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput; if (ctlfunc) (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, @@ -540,6 +545,8 @@ icmp_input(struct mbuf *m, int off) goto reflect; case ICMP_TSTAMP: + if (V_icmptstamprepl == 0) + break; if (!V_icmpbmcastecho && (m->m_flags & (M_MCAST | M_BCAST)) != 0) { ICMPSTAT_INC(icps_bmcasttstamp); @@ -597,11 +604,10 @@ icmp_input(struct mbuf *m, int off) } ifa_free(&ia->ia_ifa); reflect: - ip->ip_len += hlen; /* since ip_input deducts this */ ICMPSTAT_INC(icps_reflect); ICMPSTAT_INC(icps_outhist[icp->icmp_type]); icmp_reflect(m); - return; + return (IPPROTO_DONE); case ICMP_REDIRECT: if (V_log_redirect) { @@ -658,9 +664,6 @@ reflect: (struct sockaddr *)&icmpgw, fibnum); } pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc); -#ifdef IPSEC - key_sa_routechange((struct sockaddr *)&icmpsrc); -#endif break; /* @@ -673,16 +676,19 @@ reflect: case ICMP_TSTAMPREPLY: case ICMP_IREQREPLY: case ICMP_MASKREPLY: + case ICMP_SOURCEQUENCH: default: break; } raw: - rip_input(m, off); - return; + *mp = m; + rip_input(mp, offp, proto); + return (IPPROTO_DONE); freeit: m_freem(m); + return (IPPROTO_DONE); } /* @@ -691,12 +697,14 @@ freeit: static void icmp_reflect(struct mbuf *m) { + struct rm_priotracker in_ifa_tracker; struct ip *ip = mtod(m, struct ip *); struct ifaddr *ifa; struct ifnet *ifp; struct in_ifaddr *ia; struct in_addr t; - struct mbuf *opts = 0; + struct nhop4_extended nh_ext; + struct mbuf *opts = NULL; int optlen = (ip->ip_hl << 2) - sizeof(struct ip); if (IN_MULTICAST(ntohl(ip->ip_src.s_addr)) || @@ -707,8 +715,6 @@ icmp_reflect(struct mbuf *m) goto done; /* Ip_output() will check for broadcast */ } - m_addr_changed(m); - t = ip->ip_dst; ip->ip_dst = ip->ip_src; @@ -718,15 +724,15 @@ icmp_reflect(struct mbuf *m) * If the incoming packet was addressed directly to one of our * own addresses, use dst as the src for the reply. */ - IN_IFADDR_RLOCK(); + IN_IFADDR_RLOCK(&in_ifa_tracker); LIST_FOREACH(ia, INADDR_HASH(t.s_addr), ia_hash) { if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) { t = IA_SIN(ia)->sin_addr; - IN_IFADDR_RUNLOCK(); + IN_IFADDR_RUNLOCK(&in_ifa_tracker); goto match; } } - IN_IFADDR_RUNLOCK(); + IN_IFADDR_RUNLOCK(&in_ifa_tracker); /* * If the incoming packet was addressed to one of our broadcast @@ -791,14 +797,12 @@ icmp_reflect(struct mbuf *m) * When we don't have a route back to the packet source, stop here * and drop the packet. */ - ia = ip_rtaddr(ip->ip_dst, M_GETFIB(m)); - if (ia == NULL) { + if (fib4_lookup_nh_ext(M_GETFIB(m), ip->ip_dst, 0, 0, &nh_ext) != 0) { m_freem(m); ICMPSTAT_INC(icps_noroute); goto done; } - t = IA_SIN(ia)->sin_addr; - ifa_free(&ia->ia_ifa); + t = nh_ext.nh_src; match: #ifdef MAC mac_netinet_icmp_replyinplace(m); @@ -816,8 +820,8 @@ match: * add on any record-route or timestamp options. */ cp = (u_char *) (ip + 1); - if ((opts = ip_srcroute(m)) == 0 && - (opts = m_gethdr(M_DONTWAIT, MT_DATA))) { + if ((opts = ip_srcroute(m)) == NULL && + (opts = m_gethdr(M_NOWAIT, MT_DATA))) { opts->m_len = sizeof(struct in_addr); mtod(opts, struct in_addr *)->s_addr = 0; } @@ -865,19 +869,7 @@ match: printf("%d\n", opts->m_len); #endif } - /* - * Now strip out original options by copying rest of first - * mbuf's data back, and adjust the IP length. - */ - ip->ip_len -= optlen; - ip->ip_v = IPVERSION; - ip->ip_hl = 5; - m->m_len -= optlen; - if (m->m_flags & M_PKTHDR) - m->m_pkthdr.len -= optlen; - optlen += sizeof(struct ip); - bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1), - (unsigned)(m->m_len - sizeof(struct ip))); + ip_stripoptions(m); } m_tag_delete_nonpersistent(m); m->m_flags &= ~(M_BCAST|M_MCAST); @@ -903,7 +895,7 @@ icmp_send(struct mbuf *m, struct mbuf *opts) m->m_len -= hlen; icp = mtod(m, struct icmp *); icp->icmp_cksum = 0; - icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); + icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen); m->m_data -= hlen; m->m_len += hlen; m->m_pkthdr.rcvif = (struct ifnet *)0; @@ -919,7 +911,7 @@ icmp_send(struct mbuf *m, struct mbuf *opts) } /* - * Return milliseconds since 00:00 GMT in network format. + * Return milliseconds since 00:00 UTC in network format. */ uint32_t iptime(void) |