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