Module Name: src Committed By: maxv Date: Fri Jan 19 12:50:27 UTC 2018
Modified Files: src/sys/netinet: ip_icmp.c Log Message: Clarify icmp_error: * Rename (and constify) oiplen -> oiphlen. * Rename icmplen -> datalen, it's the size of the variable part of the ICMP header, not the total size of the ICMP header itself. * Introduce totlen, this is the total size of the ICMP header (icmp_ip included). No real functional change. To generate a diff of this commit: cvs rdiff -u -r1.161 -r1.162 src/sys/netinet/ip_icmp.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netinet/ip_icmp.c diff -u src/sys/netinet/ip_icmp.c:1.161 src/sys/netinet/ip_icmp.c:1.162 --- src/sys/netinet/ip_icmp.c:1.161 Fri Mar 31 06:49:44 2017 +++ src/sys/netinet/ip_icmp.c Fri Jan 19 12:50:27 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: ip_icmp.c,v 1.161 2017/03/31 06:49:44 ozaki-r Exp $ */ +/* $NetBSD: ip_icmp.c,v 1.162 2018/01/19 12:50:27 maxv Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -94,7 +94,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ip_icmp.c,v 1.161 2017/03/31 06:49:44 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_icmp.c,v 1.162 2018/01/19 12:50:27 maxv Exp $"); #ifdef _KERNEL_OPT #include "opt_ipsec.h" @@ -242,45 +242,55 @@ icmp_mtudisc_callback_register(void (*fu } /* - * Generate an error packet of type error - * in response to bad packet ip. + * Generate an error packet of type error in response to a bad IP packet. 'n' + * contains this packet. We create 'm' and send it. + * + * As we are not required to return everything we have, we return whatever + * we can return at ease. + * + * Note that ICMP datagrams longer than 576 octets are out of spec according + * to RFC1812; the limit on icmpreturndatabytes will keep things below that + * limit. */ void -icmp_error(struct mbuf *n, int type, int code, n_long dest, - int destmtu) +icmp_error(struct mbuf *n, int type, int code, n_long dest, int destmtu) { struct ip *oip = mtod(n, struct ip *), *nip; - unsigned oiplen = oip->ip_hl << 2; + const unsigned oiphlen = oip->ip_hl << 2; struct icmp *icp; struct mbuf *m; struct m_tag *mtag; - unsigned icmplen, mblen; + unsigned datalen, mblen, totlen; #ifdef ICMPPRINTFS if (icmpprintfs) printf("icmp_error(%p, type:%d, code:%d)\n", oip, type, code); #endif + if (type != ICMP_REDIRECT) ICMP_STATINC(ICMP_STAT_ERROR); + /* - * Don't send error if the original packet was encrypted. - * Don't send error if not the first fragment of message. - * Don't error if the old packet protocol was ICMP - * error message, only known informational types. + * Don't send error if: + * - The original packet was encrypted. + * - The packet is multicast or broadcast. + * - The packet is not the first fragment of the message. + * - The packet is an ICMP message with an unknown type. */ if (n->m_flags & M_DECRYPTED) goto freeit; + if (n->m_flags & (M_BCAST|M_MCAST)) + goto freeit; if (oip->ip_off &~ htons(IP_MF|IP_DF)) goto freeit; if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT && - n->m_len >= oiplen + ICMP_MINLEN && - !ICMP_INFOTYPE(((struct icmp *)((char *)oip + oiplen))->icmp_type)) { - ICMP_STATINC(ICMP_STAT_OLDICMP); - goto freeit; + n->m_len >= oiphlen + ICMP_MINLEN) { + struct icmp *oicp = (struct icmp *)((char *)oip + oiphlen); + if (!ICMP_INFOTYPE(oicp->icmp_type)) { + ICMP_STATINC(ICMP_STAT_OLDICMP); + goto freeit; + } } - /* Don't send error in response to a multicast or broadcast packet */ - if (n->m_flags & (M_BCAST|M_MCAST)) - goto freeit; /* * First, do a rate limitation check. @@ -291,34 +301,30 @@ icmp_error(struct mbuf *n, int type, int } /* - * Now, formulate icmp message - */ - icmplen = oiplen + min(icmpreturndatabytes, - ntohs(oip->ip_len) - oiplen); - /* - * Defend against mbuf chains shorter than oip->ip_len - oiplen: + * Compute the number of bytes we will put in 'icmp_ip'. Truncate + * it to the size of the mbuf, if it's too big. */ + datalen = oiphlen + min(icmpreturndatabytes, + ntohs(oip->ip_len) - oiphlen); mblen = 0; - for (m = n; m && (mblen < icmplen); m = m->m_next) + for (m = n; m && (mblen < datalen); m = m->m_next) mblen += m->m_len; - icmplen = min(mblen, icmplen); + datalen = min(mblen, datalen); /* - * As we are not required to return everything we have, - * we return whatever we can return at ease. - * - * Note that ICMP datagrams longer than 576 octets are out of spec - * according to RFC1812; the limit on icmpreturndatabytes below in - * icmp_sysctl will keep things below that limit. + * Compute the total length of the new packet. Truncate it if it's + * bigger than the size of a cluster. */ + CTASSERT(ICMP_MINLEN <= MCLBYTES); + if (datalen + ICMP_MINLEN > MCLBYTES) + datalen = MCLBYTES - ICMP_MINLEN; + totlen = datalen + ICMP_MINLEN; - KASSERT(ICMP_MINLEN <= MCLBYTES); - - if (icmplen + ICMP_MINLEN > MCLBYTES) - icmplen = MCLBYTES - ICMP_MINLEN; - + /* + * Allocate the mbuf for the new packet. + */ m = m_gethdr(M_DONTWAIT, MT_HEADER); - if (m && (icmplen + ICMP_MINLEN > MHLEN)) { + if (m && (totlen > MHLEN)) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); @@ -328,21 +334,31 @@ icmp_error(struct mbuf *n, int type, int if (m == NULL) goto freeit; MCLAIM(m, n->m_owner); - m->m_len = icmplen + ICMP_MINLEN; - if ((m->m_flags & M_EXT) == 0) + m->m_len = totlen; + + /* + * Advance to the ICMP header. + */ + if ((m->m_flags & M_EXT) == 0) { MH_ALIGN(m, m->m_len); - else { + } else { m->m_data += sizeof(struct ip); m->m_len -= sizeof(struct ip); } - icp = mtod(m, struct icmp *); + if ((u_int)type > ICMP_MAXTYPE) panic("icmp_error"); ICMP_STATINC(ICMP_STAT_OUTHIST + type); + + /* + * Fill in the fields of the ICMP header: icmp_type, icmp_code + * and icmp_ip. icmp_cksum gets filled later. + */ + icp = mtod(m, struct icmp *); icp->icmp_type = type; - if (type == ICMP_REDIRECT) + if (type == ICMP_REDIRECT) { icp->icmp_gwaddr.s_addr = dest; - else { + } else { icp->icmp_void = 0; /* * The following assignments assume an overlay with the @@ -355,13 +371,11 @@ icmp_error(struct mbuf *n, int type, int code == ICMP_UNREACH_NEEDFRAG && destmtu) icp->icmp_nextmtu = htons(destmtu); } - icp->icmp_code = code; - m_copydata(n, 0, icmplen, (void *)&icp->icmp_ip); + m_copydata(n, 0, datalen, (void *)&icp->icmp_ip); /* - * Now, copy old ip header (without options) - * in front of icmp message. + * Come back to the IP header. */ if ((m->m_flags & M_EXT) == 0 && m->m_data - sizeof(struct ip) < m->m_pktdat) @@ -370,6 +384,11 @@ icmp_error(struct mbuf *n, int type, int m->m_len += sizeof(struct ip); m->m_pkthdr.len = m->m_len; m_copy_rcvif(m, n); + + /* + * Now, copy the old IP header (without options) in front of the + * ICMP message. The src/dst fields will be swapped in icmp_reflect. + */ nip = mtod(m, struct ip *); /* ip_v set in ip_output */ nip->ip_hl = sizeof(struct ip) >> 2; @@ -387,6 +406,7 @@ icmp_error(struct mbuf *n, int type, int m_tag_unlink(n, mtag); m_tag_prepend(m, mtag); } + icmp_reflect(m); freeit: