From f22d98c3bc36361440a1f0038ac03c44d8393a9e Mon Sep 17 00:00:00 2001 From: Mike Karels Date: Tue, 17 Sep 1985 06:01:28 -0800 Subject: [PATCH] drop min length restriction on ip packets for errors; only one error per fragmented message; fix length calculations for short IP packets; additional arg to icmp_error for redirects; ctlinput routines take pointers to sockaddrs, not icmp's; pass interface pointer in, so can use correct return address (and dst if was sent without src address); network address mask request and reply; rearrange IP src rt options for returning ICMP request SCCS-vsn: sys/netinet/ip_icmp.c 6.13 --- usr/src/sys/netinet/ip_icmp.c | 186 +++++++++++++++++++++++++++------- 1 file changed, 148 insertions(+), 38 deletions(-) diff --git a/usr/src/sys/netinet/ip_icmp.c b/usr/src/sys/netinet/ip_icmp.c index 2120da9e82..8fca0dfc07 100644 --- a/usr/src/sys/netinet/ip_icmp.c +++ b/usr/src/sys/netinet/ip_icmp.c @@ -3,7 +3,7 @@ * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * - * @(#)ip_icmp.c 6.12 (Berkeley) %G% + * @(#)ip_icmp.c 6.13 (Berkeley) %G% */ #include "param.h" @@ -37,14 +37,17 @@ int icmpprintfs = 0; * Generate an error packet of type error * in response to bad packet ip. */ -icmp_error(oip, type, code) +/*VARARGS3*/ +icmp_error(oip, type, code, dest) struct ip *oip; int type, code; + struct in_addr dest; { register unsigned oiplen = oip->ip_hl << 2; register struct icmp *icp; struct mbuf *m; struct ip *nip; + int icmplen; #ifdef ICMPPRINTFS if (icmpprintfs) @@ -52,15 +55,13 @@ icmp_error(oip, type, code) #endif icmpstat.icps_error++; /* - * Make sure that the old IP packet had 8 bytes of data to return; - * if not, don't bother. Also don't EVER error if the old - * packet protocol was ICMP. + * Don't send error if not the first fragment of message. + * Don't EVER error if the old packet protocol was ICMP. + * (Could do ECHO, etc, but not error indications.) */ - if (oip->ip_len < 8) { - icmpstat.icps_oldshort++; + if (oip->ip_off &~ (IP_MF|IP_DF)) goto free; - } - if (oip->ip_p == IPPROTO_ICMP) { + if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) { icmpstat.icps_oldicmp++; goto free; } @@ -71,20 +72,24 @@ icmp_error(oip, type, code) m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) goto free; - m->m_len = oiplen + 8 + ICMP_MINLEN; + icmplen = oiplen + MIN(8, oip->ip_len); + m->m_len = icmplen + ICMP_MINLEN; m->m_off = MMAXOFF - m->m_len; icp = mtod(m, struct icmp *); - if ((u_int)type > ICMP_IREQREPLY) + if ((u_int)type > ICMP_MAXTYPE) panic("icmp_error"); icmpstat.icps_outhist[type]++; icp->icmp_type = type; - icp->icmp_void = 0; + if (type == ICMP_REDIRECT) + icp->icmp_gwaddr = dest; + else + icp->icmp_void = 0; if (type == ICMP_PARAMPROB) { icp->icmp_pptr = code; code = 0; } icp->icmp_code = code; - bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, oiplen + 8); + bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen); nip = &icp->icmp_ip; nip->ip_len += oiplen; nip->ip_len = htons((u_short)nip->ip_len); @@ -94,6 +99,10 @@ icmp_error(oip, type, code) * message. This allows us to reuse any source * routing info present. */ + if (m->m_len + oiplen > MLEN) + oiplen = sizeof(struct ip); + if (m->m_len + oiplen > MLEN) + panic("icmp len"); m->m_off -= oiplen; nip = mtod(m, struct ip *); bcopy((caddr_t)oip, (caddr_t)nip, oiplen); @@ -101,7 +110,7 @@ icmp_error(oip, type, code) nip->ip_p = IPPROTO_ICMP; /* icmp_send adds ip header to m_off and m_len, so we deduct here */ m->m_off += oiplen; - icmp_reflect(nip); + icmp_reflect(nip, in_ifaddr->ia_ifp); free: m_freem(dtom(oip)); @@ -110,17 +119,21 @@ free: static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP }; static struct sockaddr_in icmpsrc = { AF_INET }; static struct sockaddr_in icmpdst = { AF_INET }; +static struct sockaddr_in icmpgw = { AF_INET }; +struct in_ifaddr *ifptoia(); /* * Process a received ICMP message. */ -icmp_input(m) +icmp_input(m, ifp) struct mbuf *m; + struct ifnet *ifp; { register struct icmp *icp; register struct ip *ip = mtod(m, struct ip *); int icmplen = ip->ip_len, hlen = ip->ip_hl << 2; register int i; + struct in_ifaddr *ia; int (*ctlfunc)(), code; extern u_char ip_protox[]; extern struct in_addr in_makeaddr(); @@ -137,8 +150,7 @@ icmp_input(m) icmpstat.icps_tooshort++; goto free; } - /* THIS LENGTH CHECK STILL MISSES ANY IP OPTIONS IN ICMP_IP */ - i = MIN(icmplen, ICMP_ADVLENMIN + hlen); + i = hlen + MIN(icmplen, ICMP_ADVLENMIN); if ((m->m_off > MMAXOFF || m->m_len < i) && (m = m_pullup(m, i)) == 0) { icmpstat.icps_tooshort++; @@ -161,7 +173,7 @@ icmp_input(m) printf("icmp_input, type %d code %d\n", icp->icmp_type, icp->icmp_code); #endif - if (icp->icmp_type > ICMP_IREQREPLY) + if (icp->icmp_type > ICMP_MAXTYPE) goto free; icmpstat.icps_inhist[icp->icmp_type]++; code = icp->icmp_code; @@ -202,8 +214,9 @@ icmp_input(m) if (icmpprintfs) printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); #endif + icmpsrc.sin_addr = icp->icmp_ip.ip_dst; if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput) - (*ctlfunc)(code, (caddr_t)icp); + (*ctlfunc)(code, (struct sockaddr *)&icmpsrc); goto free; badcode: @@ -226,13 +239,25 @@ icmp_input(m) case ICMP_IREQ: #define satosin(sa) ((struct sockaddr_in *)(sa)) - if (in_netof(ip->ip_src) == 0) - ip->ip_src = in_makeaddr( - in_netof(satosin(&in_ifaddr->ia_addr)->sin_addr), + if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp))) + ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr), in_lnaof(ip->ip_src)); icp->icmp_type = ICMP_IREQREPLY; goto reflect; + case ICMP_MASKREQ: + if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0) + goto free; + icp->icmp_type = ICMP_IREQREPLY; + icp->icmp_mask = ia->ia_netmask; + if (ip->ip_src.s_addr == 0) { + if (ia->ia_ifp->if_flags & IFF_BROADCAST) + ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; + else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) + ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; + } + goto reflect; + case ICMP_REDIRECT: if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { icmpstat.icps_badlen++; @@ -245,24 +270,35 @@ icmp_input(m) * listening on a raw socket (e.g. the routing * daemon for use in updating its tables). */ + icmpgw.sin_addr = ip->ip_src; icmpdst.sin_addr = icp->icmp_gwaddr; +#ifdef ICMPPRINTFS + if (icmpprintfs) + printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst, + icp->icmp_gwaddr); +#endif if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) { icmpsrc.sin_addr = in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY); rtredirect((struct sockaddr *)&icmpsrc, - (struct sockaddr *)&icmpdst, RTF_GATEWAY); - pfctlinput(PRC_REDIRECT_NET, (caddr_t)icp); + (struct sockaddr *)&icmpdst, RTF_GATEWAY, + (struct sockaddr *)&icmpgw); + pfctlinput(PRC_REDIRECT_NET, + (struct sockaddr *)&icmpsrc); } else { icmpsrc.sin_addr = icp->icmp_ip.ip_dst; rtredirect((struct sockaddr *)&icmpsrc, - (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST); - pfctlinput(PRC_REDIRECT_HOST, (caddr_t)icp); + (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST, + (struct sockaddr *)&icmpgw); + pfctlinput(PRC_REDIRECT_HOST, + (struct sockaddr *)&icmpsrc); } /* FALL THROUGH */ case ICMP_ECHOREPLY: case ICMP_TSTAMPREPLY: case ICMP_IREQREPLY: + case ICMP_MASKREPLY: icmpsrc.sin_addr = ip->ip_src; icmpdst.sin_addr = ip->ip_dst; raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc, @@ -276,7 +312,7 @@ reflect: ip->ip_len += hlen; /* since ip_input deducts this */ icmpstat.icps_reflect++; icmpstat.icps_outhist[icp->icmp_type]++; - icmp_reflect(ip); + icmp_reflect(ip, ifp); return; free: m_freem(dtom(ip)); @@ -284,28 +320,102 @@ free: /* * Reflect the ip packet back to the source - * TODO: rearrange ip source routing options. */ -icmp_reflect(ip) +icmp_reflect(ip, ifp) struct ip *ip; + struct ifnet *ifp; { - register struct in_addr t; register struct in_ifaddr *ia; + register u_char *cp; + int opt, optlen, cnt; + struct in_addr t; + struct in_addr *p, *q; t = ip->ip_dst; - if (t.s_addr == INADDR_ANY) - t = IA_SIN(in_ifaddr)->sin_addr; - else for (ia = in_ifaddr; ia; ia = ia->ia_next) - if (t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr && - (ia->ia_ifp->if_flags & IFF_BROADCAST)) { - t = IA_SIN(ia)->sin_addr; - break; - } ip->ip_dst = ip->ip_src; + /* + * If the incoming packet was addressed directly to us, + * use dst as the src for the reply. Otherwise (broadcast + * or anonymous), use the address which corresponds + * to the incoming interface. + */ + for (ia = in_ifaddr; ia; ia = ia->ia_next) { + if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) + break; + if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && + t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr) + break; + } + if (ia == (struct in_ifaddr *)0) + ia = ifptoia(ifp); + if (ia) + t = IA_SIN(ia)->sin_addr; ip->ip_src = t; + + /* + * If the incoming packet was source-routed, + * we need to reverse the route and set the next-hop destination. + * We can dispense with the error checking + * as ip_dooptions has been through here before. + */ + if ((ip->ip_hl << 2) > sizeof(struct ip)) { + cp = (u_char *)(ip + 1); + cnt = (ip->ip_hl << 2) - sizeof (struct ip); + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[IPOPT_OPTVAL]; + if (opt == IPOPT_EOL) + break; + if (opt == IPOPT_NOP) + optlen = 1; + else + optlen = cp[IPOPT_OLEN]; + if (opt == IPOPT_LSRR || opt == IPOPT_SSRR) { + p = (struct in_addr *) + (cp + cp[IPOPT_OFFSET] - 1) - 1; + q = (struct in_addr *)(cp + IPOPT_MINOFF - 1); + /* + * First switch the last route entry + * (first hop for return) with ip_dst + * (final hop on return). + */ + bcopy((caddr_t)p, (caddr_t)&t, sizeof(t)); + bcopy((caddr_t)&ip->ip_dst, (caddr_t)p, + sizeof(struct in_addr)); + ip->ip_dst = t; + p--; + /* + * Then reverse remaining route entries. + */ + while (p > q) { + bcopy((caddr_t)p, (caddr_t)&t, + sizeof(struct in_addr)); + bcopy((caddr_t)q, (caddr_t)p, + sizeof(struct in_addr)); + bcopy((caddr_t)&t, (caddr_t)q, + sizeof(struct in_addr)); + p--; + q++; + } + cp[IPOPT_OFFSET] = IPOPT_MINOFF; + break; + } + } + } icmp_send(ip); } +struct in_ifaddr * +ifptoia(ifp) + struct ifnet *ifp; +{ + register struct in_ifaddr *ia; + + for (ia = in_ifaddr; ia; ia = ia->ia_next) + if (ia->ia_ifp == ifp) + return (ia); + return ((struct in_ifaddr *)0); +} + /* * Send an icmp packet back to the ip level, * after supplying a checksum. -- 2.20.1