+ bcopy(opts + olen, opts, (unsigned)i);
+ m->m_len -= olen;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
+
+u_char inetctlerrmap[PRC_NCMDS] = {
+ 0, 0, 0, 0,
+ 0, 0, EHOSTDOWN, EHOSTUNREACH,
+ ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
+ EMSGSIZE, EHOSTUNREACH, 0, 0,
+ 0, 0, 0, 0,
+ ENOPROTOOPT
+};
+
+#ifndef IPFORWARDING
+#define IPFORWARDING 1
+#endif
+#ifndef IPSENDREDIRECTS
+#define IPSENDREDIRECTS 1
+#endif
+int ipprintfs = 0;
+int ipforwarding = IPFORWARDING;
+extern int in_interfaces;
+int ipsendredirects = IPSENDREDIRECTS;
+
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repertoire
+ * of codes and types.
+ *
+ * If not forwarding (possibly because we have only a single external
+ * network), just drop the packet. This could be confusing if ipforwarding
+ * was zero but some routing protocol was advancing us as a gateway
+ * to somewhere. However, we must let the routing protocol deal with that.
+ */
+ip_forward(ip, ifp)
+ register struct ip *ip;
+ struct ifnet *ifp;
+{
+ register int error, type = 0, code;
+ register struct sockaddr_in *sin;
+ struct mbuf *mcopy;
+ struct in_addr dest;
+
+ dest.s_addr = 0;
+ if (ipprintfs)
+ printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
+ ip->ip_dst, ip->ip_ttl);
+ ip->ip_id = htons(ip->ip_id);
+ if (ipforwarding == 0 || in_interfaces <= 1) {
+ ipstat.ips_cantforward++;
+#ifdef GATEWAY
+ type = ICMP_UNREACH, code = ICMP_UNREACH_NET;
+ goto sendicmp;
+#else
+ m_freem(dtom(ip));
+ return;
+#endif
+ }
+ if (ip->ip_ttl < IPTTLDEC) {
+ type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
+ goto sendicmp;
+ }
+ ip->ip_ttl -= IPTTLDEC;
+
+ /*
+ * Save at most 64 bytes of the packet in case
+ * we need to generate an ICMP message to the src.
+ */
+ mcopy = m_copy(dtom(ip), 0, imin(ip->ip_len, 64));
+
+ sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
+ if (ipforward_rt.ro_rt == 0 ||
+ ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
+ if (ipforward_rt.ro_rt) {
+ RTFREE(ipforward_rt.ro_rt);
+ ipforward_rt.ro_rt = 0;
+ }
+ sin->sin_family = AF_INET;
+ sin->sin_addr = ip->ip_dst;
+
+ rtalloc(&ipforward_rt);
+ }
+ /*
+ * If forwarding packet using same interface that it came in on,
+ * perhaps should send a redirect to sender to shortcut a hop.
+ * Only send redirect if source is sending directly to us,
+ * and if packet was not source routed (or has any options).
+ */
+ if (ipforward_rt.ro_rt && ipforward_rt.ro_rt->rt_ifp == ifp &&
+ ipsendredirects && ip->ip_hl == (sizeof(struct ip) >> 2)) {
+ struct in_ifaddr *ia;
+ extern struct in_ifaddr *ifptoia();
+ u_long src = ntohl(ip->ip_src.s_addr);
+ u_long dst = ntohl(ip->ip_dst.s_addr);
+
+ if ((ia = ifptoia(ifp)) &&
+ (src & ia->ia_subnetmask) == ia->ia_subnet) {
+ if (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY)
+ dest = satosin(&ipforward_rt.ro_rt->rt_gateway)->sin_addr;
+ else
+ dest = ip->ip_dst;
+ /*
+ * If the destination is reached by a route to host,
+ * is on a subnet of a local net, or is directly
+ * on the attached net (!), use host redirect.
+ * (We may be the correct first hop for other subnets.)
+ */
+ type = ICMP_REDIRECT;
+ code = ICMP_REDIRECT_NET;
+ if ((ipforward_rt.ro_rt->rt_flags & RTF_HOST) ||
+ (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY) == 0)
+ code = ICMP_REDIRECT_HOST;
+ else for (ia = in_ifaddr; ia = ia->ia_next; )
+ if ((dst & ia->ia_netmask) == ia->ia_net) {
+ if (ia->ia_subnetmask != ia->ia_netmask)
+ code = ICMP_REDIRECT_HOST;
+ break;
+ }
+ if (ipprintfs)
+ printf("redirect (%d) to %x\n", code, dest);
+ }
+ }
+
+ error = ip_output(dtom(ip), (struct mbuf *)0, &ipforward_rt,
+ IP_FORWARDING);
+ if (error)
+ ipstat.ips_cantforward++;
+ else if (type)
+ ipstat.ips_redirectsent++;
+ else {
+ if (mcopy)
+ m_freem(mcopy);
+ ipstat.ips_forward++;
+ return;
+ }
+ if (mcopy == NULL)
+ return;
+ ip = mtod(mcopy, struct ip *);
+ type = ICMP_UNREACH;
+ switch (error) {
+
+ case 0: /* forwarded, but need redirect */
+ type = ICMP_REDIRECT;
+ /* code set above */
+ break;
+
+ case ENETUNREACH:
+ case ENETDOWN:
+ code = ICMP_UNREACH_NET;
+ break;
+
+ case EMSGSIZE:
+ code = ICMP_UNREACH_NEEDFRAG;
+ break;
+
+ case EPERM:
+ code = ICMP_UNREACH_PORT;
+ break;
+
+ case ENOBUFS:
+ type = ICMP_SOURCEQUENCH;
+ break;
+
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ code = ICMP_UNREACH_HOST;
+ break;
+ }
+sendicmp:
+ icmp_error(ip, type, code, ifp, dest);