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