xref: /original-bsd/sys/netinet/ip_output.c (revision e74403ba)
1 /*	ip_output.c	6.3	83/12/15	*/
2 
3 #include "../h/param.h"
4 #include "../h/mbuf.h"
5 #include "../h/errno.h"
6 #include "../h/socket.h"
7 #include "../h/socketvar.h"
8 
9 #include "../net/if.h"
10 #include "../net/route.h"
11 
12 #include "../netinet/in.h"
13 #include "../netinet/in_systm.h"
14 #include "../netinet/ip.h"
15 #include "../netinet/ip_var.h"
16 
17 #ifdef vax
18 #include "../vax/mtpr.h"
19 #endif
20 
21 ip_output(m, opt, ro, flags)
22 	struct mbuf *m;
23 	struct mbuf *opt;
24 	struct route *ro;
25 	int flags;
26 {
27 	register struct ip *ip = mtod(m, struct ip *);
28 	register struct ifnet *ifp;
29 	int len, hlen = sizeof (struct ip), off, error = 0;
30 	struct route iproute;
31 	struct sockaddr *dst;
32 
33 	if (opt)				/* XXX */
34 		(void) m_free(opt);		/* XXX */
35 	/*
36 	 * Fill in IP header.
37 	 */
38 	ip->ip_hl = hlen >> 2;
39 	if ((flags & IP_FORWARDING) == 0) {
40 		ip->ip_v = IPVERSION;
41 		ip->ip_off &= IP_DF;
42 		ip->ip_id = htons(ip_id++);
43 	}
44 
45 	/*
46 	 * Route packet.
47 	 */
48 	if (ro == 0) {
49 		ro = &iproute;
50 		bzero((caddr_t)ro, sizeof (*ro));
51 	}
52 	dst = &ro->ro_dst;
53 	if (ro->ro_rt == 0) {
54 		ro->ro_dst.sa_family = AF_INET;
55 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr = ip->ip_dst;
56 		/*
57 		 * If routing to interface only,
58 		 * short circuit routing lookup.
59 		 */
60 		if (flags & IP_ROUTETOIF) {
61 			ifp = if_ifonnetof(in_netof(ip->ip_dst));
62 			if (ifp == 0) {
63 				error = ENETUNREACH;
64 				goto bad;
65 			}
66 			goto gotif;
67 		}
68 		rtalloc(ro);
69 	} else if (ro->ro_rt->rt_flags & RTF_UP == 0) {
70 		/*
71 		 * The old route has gone away; try for a new one.
72 		 */
73 		rtfree(ro->ro_rt);
74 		rtalloc(ro);
75 	}
76 	if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
77 		error = ENETUNREACH;
78 		goto bad;
79 	}
80 	ro->ro_rt->rt_use++;
81 	if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
82 		dst = &ro->ro_rt->rt_gateway;
83 gotif:
84 #ifndef notdef
85 	/*
86 	 * If source address not specified yet, use address
87 	 * of outgoing interface.
88 	 */
89 	if (in_lnaof(ip->ip_src) == INADDR_ANY)
90 		ip->ip_src.s_addr =
91 		    ((struct sockaddr_in *)&ifp->if_addr)->sin_addr.s_addr;
92 #endif
93 
94 	/*
95 	 * Look for broadcast address and
96 	 * and verify user is allowed to send
97 	 * such a packet.
98 	 */
99 	if (in_lnaof(((struct sockaddr_in *)dst)->sin_addr) == INADDR_ANY) {
100 		if ((ifp->if_flags & IFF_BROADCAST) == 0) {
101 			error = EADDRNOTAVAIL;
102 			goto bad;
103 		}
104 		if ((flags & IP_ALLOWBROADCAST) == 0) {
105 			error = EACCES;
106 			goto bad;
107 		}
108 		/* don't allow broadcast messages to be fragmented */
109 		if (ip->ip_len > ifp->if_mtu) {
110 			error = EMSGSIZE;
111 			goto bad;
112 		}
113 	}
114 
115 	/*
116 	 * If small enough for interface, can just send directly.
117 	 */
118 	if (ip->ip_len <= ifp->if_mtu) {
119 		ip->ip_len = htons((u_short)ip->ip_len);
120 		ip->ip_off = htons((u_short)ip->ip_off);
121 		ip->ip_sum = 0;
122 		ip->ip_sum = in_cksum(m, hlen);
123 		error = (*ifp->if_output)(ifp, m, dst);
124 		goto done;
125 	}
126 
127 	/*
128 	 * Too large for interface; fragment if possible.
129 	 * Must be able to put at least 8 bytes per fragment.
130 	 */
131 	if (ip->ip_off & IP_DF) {
132 		error = EMSGSIZE;
133 		goto bad;
134 	}
135 	len = (ifp->if_mtu - hlen) &~ 7;
136 	if (len < 8) {
137 		error = EMSGSIZE;
138 		goto bad;
139 	}
140 
141 	/*
142 	 * Discard IP header from logical mbuf for m_copy's sake.
143 	 * Loop through length of segment, make a copy of each
144 	 * part and output.
145 	 */
146 	m->m_len -= sizeof (struct ip);
147 	m->m_off += sizeof (struct ip);
148 	for (off = 0; off < ip->ip_len-hlen; off += len) {
149 		struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER);
150 		struct ip *mhip;
151 
152 		if (mh == 0) {
153 			error = ENOBUFS;
154 			goto bad;
155 		}
156 		mh->m_off = MMAXOFF - hlen;
157 		mhip = mtod(mh, struct ip *);
158 		*mhip = *ip;
159 		if (hlen > sizeof (struct ip)) {
160 			int olen = ip_optcopy(ip, mhip, off);
161 			mh->m_len = sizeof (struct ip) + olen;
162 		} else
163 			mh->m_len = sizeof (struct ip);
164 		mhip->ip_off = off >> 3;
165 		if (off + len >= ip->ip_len-hlen)
166 			len = mhip->ip_len = ip->ip_len - hlen - off;
167 		else {
168 			mhip->ip_len = len;
169 			mhip->ip_off |= IP_MF;
170 		}
171 		mhip->ip_len += sizeof (struct ip);
172 		mhip->ip_len = htons((u_short)mhip->ip_len);
173 		mh->m_next = m_copy(m, off, len);
174 		if (mh->m_next == 0) {
175 			(void) m_free(mh);
176 			error = ENOBUFS;	/* ??? */
177 			goto bad;
178 		}
179 		mhip->ip_off = htons((u_short)mhip->ip_off);
180 		mhip->ip_sum = 0;
181 		mhip->ip_sum = in_cksum(mh, hlen);
182 		if (error = (*ifp->if_output)(ifp, mh, dst))
183 			break;
184 	}
185 	m_freem(m);
186 	goto done;
187 
188 bad:
189 	m_freem(m);
190 done:
191 	if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
192 		RTFREE(ro->ro_rt);
193 	return (error);
194 }
195 
196 /*
197  * Copy options from ip to jp.
198  * If off is 0 all options are copied
199  * otherwise copy selectively.
200  */
201 ip_optcopy(ip, jp, off)
202 	struct ip *ip, *jp;
203 	int off;
204 {
205 	register u_char *cp, *dp;
206 	int opt, optlen, cnt;
207 
208 	cp = (u_char *)(ip + 1);
209 	dp = (u_char *)(jp + 1);
210 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
211 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
212 		opt = cp[0];
213 		if (opt == IPOPT_EOL)
214 			break;
215 		if (opt == IPOPT_NOP)
216 			optlen = 1;
217 		else
218 			optlen = cp[1];
219 		if (optlen > cnt)			/* XXX */
220 			optlen = cnt;			/* XXX */
221 		if (off == 0 || IPOPT_COPIED(opt)) {
222 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
223 			dp += optlen;
224 		}
225 	}
226 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
227 		*dp++ = IPOPT_EOL;
228 	return (optlen);
229 }
230