+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+ip_output(m0, opt, ro, flags)
+ struct mbuf *m0;
+ struct mbuf *opt;
+ struct route *ro;
+ int flags;
+{
+ register struct ip *ip, *mhip;
+ register struct ifnet *ifp;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof (struct ip);
+ int len, off, error = 0;
+ struct route iproute;
+ struct sockaddr_in *dst;
+ struct in_ifaddr *ia;
+
+if ((m->m_flags & M_PKTHDR) == 0)
+panic("ip_output no HDR");
+ if (opt) {
+ m = ip_insertoptions(m, opt, &len);
+ hlen = len;
+ }
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ if ((flags & IP_FORWARDING) == 0) {
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_hl = hlen >> 2;
+ } else {
+ hlen = ip->ip_hl << 2;
+ ipstat.ips_localout++;
+ }
+ /*
+ * Route packet.
+ */
+ if (ro == 0) {
+ ro = &iproute;
+ bzero((caddr_t)ro, sizeof (*ro));
+ }
+ dst = (struct sockaddr_in *)&ro->ro_dst;
+ /*
+ * If there is a cached route,
+ * check that it is to the same destination
+ * and is still up. If not, free it and try again.
+ */
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == 0) {
+ dst->sin_family = AF_INET;
+ dst->sin_len = sizeof(*dst);
+ dst->sin_addr = ip->ip_dst;
+ }
+ /*
+ * If routing to interface only,
+ * short circuit routing lookup.
+ */
+ if (flags & IP_ROUTETOIF) {
+
+ ia = (struct in_ifaddr *)ifa_ifwithdstaddr((struct sockaddr *)dst);
+ if (ia == 0)
+ ia = in_iaonnetof(in_netof(ip->ip_dst));
+ if (ia == 0) {
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ifp = ia->ia_ifp;
+ } else {
+ if (ro->ro_rt == 0)
+ rtalloc(ro);
+ if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
+ if (in_localaddr(ip->ip_dst))
+ error = EHOSTUNREACH;
+ else
+ error = ENETUNREACH;
+ goto bad;
+ }
+ ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa;
+ ro->ro_rt->rt_use++;
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;
+ }
+#ifndef notdef
+ /*
+ * If source address not specified yet, use address
+ * of outgoing interface.
+ */
+ if (ip->ip_src.s_addr == INADDR_ANY)
+ ip->ip_src = IA_SIN(ia)->sin_addr;
+#endif
+ /*
+ * Look for broadcast address and
+ * and verify user is allowed to send
+ * such a packet.
+ */
+ if (in_broadcast(dst->sin_addr)) {
+ if ((ifp->if_flags & IFF_BROADCAST) == 0) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ if ((flags & IP_ALLOWBROADCAST) == 0) {
+ error = EACCES;
+ goto bad;
+ }
+ /* don't allow broadcast messages to be fragmented */
+ if (ip->ip_len > ifp->if_mtu) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ m->m_flags |= M_BCAST;
+ }