xref: /csrg-svn/sys/netinet/ip_output.c (revision 32787)
1 /*
2  * Copyright (c) 1982, 1986 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  *	@(#)ip_output.c	7.7 (Berkeley) 12/07/87
13  */
14 
15 #include "param.h"
16 #include "mbuf.h"
17 #include "errno.h"
18 #include "protosw.h"
19 #include "socket.h"
20 #include "socketvar.h"
21 
22 #include "../net/if.h"
23 #include "../net/route.h"
24 
25 #include "in.h"
26 #include "in_pcb.h"
27 #include "in_systm.h"
28 #include "in_var.h"
29 #include "ip.h"
30 #include "ip_var.h"
31 
32 #ifdef vax
33 #include "../machine/mtpr.h"
34 #endif
35 
36 struct mbuf *ip_insertoptions();
37 
38 /*
39  * IP output.  The packet in mbuf chain m contains a skeletal IP
40  * header (with len, off, ttl, proto, tos, src, dst).
41  * The mbuf chain containing the packet will be freed.
42  * The mbuf opt, if present, will not be freed.
43  */
44 ip_output(m, opt, ro, flags)
45 	struct mbuf *m;
46 	struct mbuf *opt;
47 	struct route *ro;
48 	int flags;
49 {
50 	register struct ip *ip;
51 	register struct ifnet *ifp;
52 	int len, hlen = sizeof (struct ip), off, error = 0;
53 	struct route iproute;
54 	struct sockaddr_in *dst;
55 
56 	if (opt)
57 		m = ip_insertoptions(m, opt, &hlen);
58 	ip = mtod(m, struct ip *);
59 	/*
60 	 * Fill in IP header.
61 	 */
62 	if ((flags & IP_FORWARDING) == 0) {
63 		ip->ip_v = IPVERSION;
64 		ip->ip_off &= IP_DF;
65 		ip->ip_id = htons(ip_id++);
66 		ip->ip_hl = hlen >> 2;
67 	} else
68 		hlen = ip->ip_hl << 2;
69 
70 	/*
71 	 * Route packet.
72 	 */
73 	if (ro == 0) {
74 		ro = &iproute;
75 		bzero((caddr_t)ro, sizeof (*ro));
76 	}
77 	dst = (struct sockaddr_in *)&ro->ro_dst;
78 	/*
79 	 * If there is a cached route,
80 	 * check that it is to the same destination
81 	 * and is still up.  If not, free it and try again.
82 	 */
83 	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
84 	   dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
85 		RTFREE(ro->ro_rt);
86 		ro->ro_rt = (struct rtentry *)0;
87 	}
88 	if (ro->ro_rt == 0) {
89 		dst->sin_family = AF_INET;
90 		dst->sin_addr = ip->ip_dst;
91 	}
92 	/*
93 	 * If routing to interface only,
94 	 * short circuit routing lookup.
95 	 */
96 	if (flags & IP_ROUTETOIF) {
97 		struct in_ifaddr *ia;
98 
99 		ia = (struct in_ifaddr *)ifa_ifwithdstaddr(dst);
100 		if (ia == 0)
101 			ia = in_iaonnetof(in_netof(ip->ip_dst));
102 		if (ia == 0) {
103 			error = ENETUNREACH;
104 			goto bad;
105 		}
106 		ifp = ia->ia_ifp;
107 	} else {
108 		if (ro->ro_rt == 0)
109 			rtalloc(ro);
110 		if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
111 			if (in_localaddr(ip->ip_dst))
112 				error = EHOSTUNREACH;
113 			else
114 				error = ENETUNREACH;
115 			goto bad;
116 		}
117 		ro->ro_rt->rt_use++;
118 		if (ro->ro_rt->rt_flags & RTF_GATEWAY)
119 			dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway;
120 	}
121 #ifndef notdef
122 	/*
123 	 * If source address not specified yet, use address
124 	 * of outgoing interface.
125 	 */
126 	if (ip->ip_src.s_addr == INADDR_ANY) {
127 		register struct in_ifaddr *ia;
128 
129 		for (ia = in_ifaddr; ia; ia = ia->ia_next)
130 			if (ia->ia_ifp == ifp) {
131 				ip->ip_src = IA_SIN(ia)->sin_addr;
132 				break;
133 			}
134 	}
135 #endif
136 	/*
137 	 * Look for broadcast address and
138 	 * and verify user is allowed to send
139 	 * such a packet.
140 	 */
141 	if (in_broadcast(dst->sin_addr)) {
142 		if ((ifp->if_flags & IFF_BROADCAST) == 0) {
143 			error = EADDRNOTAVAIL;
144 			goto bad;
145 		}
146 		if ((flags & IP_ALLOWBROADCAST) == 0) {
147 			error = EACCES;
148 			goto bad;
149 		}
150 		/* don't allow broadcast messages to be fragmented */
151 		if (ip->ip_len > ifp->if_mtu) {
152 			error = EMSGSIZE;
153 			goto bad;
154 		}
155 	}
156 
157 	/*
158 	 * If small enough for interface, can just send directly.
159 	 */
160 	if (ip->ip_len <= ifp->if_mtu) {
161 		ip->ip_len = htons((u_short)ip->ip_len);
162 		ip->ip_off = htons((u_short)ip->ip_off);
163 		ip->ip_sum = 0;
164 		ip->ip_sum = in_cksum(m, hlen);
165 		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst);
166 		goto done;
167 	}
168 
169 	/*
170 	 * Too large for interface; fragment if possible.
171 	 * Must be able to put at least 8 bytes per fragment.
172 	 */
173 	if (ip->ip_off & IP_DF) {
174 		error = EMSGSIZE;
175 		goto bad;
176 	}
177 	len = (ifp->if_mtu - hlen) &~ 7;
178 	if (len < 8) {
179 		error = EMSGSIZE;
180 		goto bad;
181 	}
182 
183 	/*
184 	 * Discard IP header from logical mbuf for m_copy's sake.
185 	 * Loop through length of segment, make a copy of each
186 	 * part and output.
187 	 */
188 	m->m_len -= sizeof (struct ip);
189 	m->m_off += sizeof (struct ip);
190 	for (off = 0; off < ip->ip_len-hlen; off += len) {
191 		struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER);
192 		struct ip *mhip;
193 
194 		if (mh == 0) {
195 			error = ENOBUFS;
196 			goto bad;
197 		}
198 		mh->m_off = MMAXOFF - hlen;
199 		mhip = mtod(mh, struct ip *);
200 		*mhip = *ip;
201 		if (hlen > sizeof (struct ip)) {
202 			int olen = ip_optcopy(ip, mhip, off);
203 			mh->m_len = sizeof (struct ip) + olen;
204 		} else
205 			mh->m_len = sizeof (struct ip);
206 		mhip->ip_off = (off >> 3) + (ip->ip_off & ~IP_MF);
207 		if (ip->ip_off & IP_MF)
208 			mhip->ip_off |= IP_MF;
209 		if (off + len >= ip->ip_len-hlen)
210 			len = mhip->ip_len = ip->ip_len - hlen - off;
211 		else {
212 			mhip->ip_len = len;
213 			mhip->ip_off |= IP_MF;
214 		}
215 		mhip->ip_len += sizeof (struct ip);
216 		mhip->ip_len = htons((u_short)mhip->ip_len);
217 		mh->m_next = m_copy(m, off, len);
218 		if (mh->m_next == 0) {
219 			(void) m_free(mh);
220 			error = ENOBUFS;	/* ??? */
221 			goto bad;
222 		}
223 		mhip->ip_off = htons((u_short)mhip->ip_off);
224 		mhip->ip_sum = 0;
225 		mhip->ip_sum = in_cksum(mh, hlen);
226 		if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst))
227 			break;
228 	}
229 bad:
230 	m_freem(m);
231 done:
232 	if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
233 		RTFREE(ro->ro_rt);
234 	return (error);
235 }
236 
237 /*
238  * Insert IP options into preformed packet.
239  * Adjust IP destination as required for IP source routing,
240  * as indicated by a non-zero in_addr at the start of the options.
241  */
242 struct mbuf *
243 ip_insertoptions(m, opt, phlen)
244 	register struct mbuf *m;
245 	struct mbuf *opt;
246 	int *phlen;
247 {
248 	register struct ipoption *p = mtod(opt, struct ipoption *);
249 	struct mbuf *n;
250 	register struct ip *ip = mtod(m, struct ip *);
251 	unsigned optlen;
252 
253 	optlen = opt->m_len - sizeof(p->ipopt_dst);
254 	if (p->ipopt_dst.s_addr)
255 		ip->ip_dst = p->ipopt_dst;
256 	if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) {
257 		MGET(n, M_DONTWAIT, MT_HEADER);
258 		if (n == 0)
259 			return (m);
260 		m->m_len -= sizeof(struct ip);
261 		m->m_off += sizeof(struct ip);
262 		n->m_next = m;
263 		m = n;
264 		m->m_off = MMAXOFF - sizeof(struct ip) - optlen;
265 		m->m_len = optlen + sizeof(struct ip);
266 		bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
267 	} else {
268 		m->m_off -= optlen;
269 		m->m_len += optlen;
270 		ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
271 	}
272 	ip = mtod(m, struct ip *);
273 	bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);
274 	*phlen = sizeof(struct ip) + optlen;
275 	ip->ip_len += optlen;
276 	return (m);
277 }
278 
279 /*
280  * Copy options from ip to jp.
281  * If off is 0 all options are copied
282  * otherwise copy selectively.
283  */
284 ip_optcopy(ip, jp, off)
285 	struct ip *ip, *jp;
286 	int off;
287 {
288 	register u_char *cp, *dp;
289 	int opt, optlen, cnt;
290 
291 	cp = (u_char *)(ip + 1);
292 	dp = (u_char *)(jp + 1);
293 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
294 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
295 		opt = cp[0];
296 		if (opt == IPOPT_EOL)
297 			break;
298 		if (opt == IPOPT_NOP)
299 			optlen = 1;
300 		else
301 			optlen = cp[IPOPT_OLEN];
302 		if (optlen > cnt)			/* XXX */
303 			optlen = cnt;			/* XXX */
304 		if (off == 0 || IPOPT_COPIED(opt)) {
305 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
306 			dp += optlen;
307 		}
308 	}
309 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
310 		*dp++ = IPOPT_EOL;
311 	return (optlen);
312 }
313 
314 /*
315  * IP socket option processing.
316  */
317 ip_ctloutput(op, so, level, optname, m)
318 	int op;
319 	struct socket *so;
320 	int level, optname;
321 	struct mbuf **m;
322 {
323 	int error = 0;
324 	struct inpcb *inp = sotoinpcb(so);
325 
326 	if (level != IPPROTO_IP)
327 		error = EINVAL;
328 	else switch (op) {
329 
330 	case PRCO_SETOPT:
331 		switch (optname) {
332 		case IP_OPTIONS:
333 			return (ip_pcbopts(&inp->inp_options, *m));
334 
335 		default:
336 			error = EINVAL;
337 			break;
338 		}
339 		break;
340 
341 	case PRCO_GETOPT:
342 		switch (optname) {
343 		case IP_OPTIONS:
344 			*m = m_get(M_WAIT, MT_SOOPTS);
345 			if (inp->inp_options) {
346 				(*m)->m_off = inp->inp_options->m_off;
347 				(*m)->m_len = inp->inp_options->m_len;
348 				bcopy(mtod(inp->inp_options, caddr_t),
349 				    mtod(*m, caddr_t), (unsigned)(*m)->m_len);
350 			} else
351 				(*m)->m_len = 0;
352 			break;
353 		default:
354 			error = EINVAL;
355 			break;
356 		}
357 		break;
358 	}
359 	if (op == PRCO_SETOPT && *m)
360 		(void)m_free(*m);
361 	return (error);
362 }
363 
364 /*
365  * Set up IP options in pcb for insertion in output packets.
366  * Store in mbuf with pointer in pcbopt, adding pseudo-option
367  * with destination address if source routed.
368  */
369 ip_pcbopts(pcbopt, m)
370 	struct mbuf **pcbopt;
371 	register struct mbuf *m;
372 {
373 	register cnt, optlen;
374 	register u_char *cp;
375 	u_char opt;
376 
377 	/* turn off any old options */
378 	if (*pcbopt)
379 		(void)m_free(*pcbopt);
380 	*pcbopt = 0;
381 	if (m == (struct mbuf *)0 || m->m_len == 0) {
382 		/*
383 		 * Only turning off any previous options.
384 		 */
385 		if (m)
386 			(void)m_free(m);
387 		return (0);
388 	}
389 
390 #ifndef	vax
391 	if (m->m_len % sizeof(long))
392 		goto bad;
393 #endif
394 	/*
395 	 * IP first-hop destination address will be stored before
396 	 * actual options; move other options back
397 	 * and clear it when none present.
398 	 */
399 #if	MAX_IPOPTLEN >= MMAXOFF - MMINOFF
400 	if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN)
401 		goto bad;
402 #else
403 	if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF)
404 		goto bad;
405 #endif
406 	cnt = m->m_len;
407 	m->m_len += sizeof(struct in_addr);
408 	cp = mtod(m, u_char *) + sizeof(struct in_addr);
409 	ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt);
410 	bzero(mtod(m, caddr_t), sizeof(struct in_addr));
411 
412 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
413 		opt = cp[IPOPT_OPTVAL];
414 		if (opt == IPOPT_EOL)
415 			break;
416 		if (opt == IPOPT_NOP)
417 			optlen = 1;
418 		else {
419 			optlen = cp[IPOPT_OLEN];
420 			if (optlen <= IPOPT_OLEN || optlen > cnt)
421 				goto bad;
422 		}
423 		switch (opt) {
424 
425 		default:
426 			break;
427 
428 		case IPOPT_LSRR:
429 		case IPOPT_SSRR:
430 			/*
431 			 * user process specifies route as:
432 			 *	->A->B->C->D
433 			 * D must be our final destination (but we can't
434 			 * check that since we may not have connected yet).
435 			 * A is first hop destination, which doesn't appear in
436 			 * actual IP option, but is stored before the options.
437 			 */
438 			if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
439 				goto bad;
440 			m->m_len -= sizeof(struct in_addr);
441 			cnt -= sizeof(struct in_addr);
442 			optlen -= sizeof(struct in_addr);
443 			cp[IPOPT_OLEN] = optlen;
444 			/*
445 			 * Move first hop before start of options.
446 			 */
447 			bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
448 			    sizeof(struct in_addr));
449 			/*
450 			 * Then copy rest of options back
451 			 * to close up the deleted entry.
452 			 */
453 			ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] +
454 			    sizeof(struct in_addr)),
455 			    (caddr_t)&cp[IPOPT_OFFSET+1],
456 			    (unsigned)cnt + sizeof(struct in_addr));
457 			break;
458 		}
459 	}
460 	*pcbopt = m;
461 	return (0);
462 
463 bad:
464 	(void)m_free(m);
465 	return (EINVAL);
466 }
467