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