xref: /csrg-svn/sys/netinet/ip_output.c (revision 8532)
1 /*	ip_output.c	1.37	82/10/13	*/
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 #if vax
96 		ip->ip_len = htons((u_short)ip->ip_len);
97 		ip->ip_off = htons((u_short)ip->ip_off);
98 #endif
99 		ip->ip_sum = 0;
100 		ip->ip_sum = in_cksum(m, hlen);
101 		error = (*ifp->if_output)(ifp, m, dst);
102 		goto done;
103 	}
104 
105 	/*
106 	 * Too large for interface; fragment if possible.
107 	 * Must be able to put at least 8 bytes per fragment.
108 	 */
109 	if (ip->ip_off & IP_DF) {
110 		error = EMSGSIZE;
111 		goto bad;
112 	}
113 	len = (ifp->if_mtu - hlen) &~ 7;
114 	if (len < 8) {
115 		error = EMSGSIZE;
116 		goto bad;
117 	}
118 
119 	/*
120 	 * Discard IP header from logical mbuf for m_copy's sake.
121 	 * Loop through length of segment, make a copy of each
122 	 * part and output.
123 	 */
124 	m->m_len -= sizeof (struct ip);
125 	m->m_off += sizeof (struct ip);
126 	for (off = 0; off < ip->ip_len-hlen; off += len) {
127 		struct mbuf *mh = m_get(M_DONTWAIT);
128 		struct ip *mhip;
129 
130 		if (mh == 0) {
131 			error = ENOBUFS;
132 			goto bad;
133 		}
134 		mh->m_off = MMAXOFF - hlen;
135 		mhip = mtod(mh, struct ip *);
136 		*mhip = *ip;
137 		if (hlen > sizeof (struct ip)) {
138 			int olen = ip_optcopy(ip, mhip, off);
139 			mh->m_len = sizeof (struct ip) + olen;
140 		} else
141 			mh->m_len = sizeof (struct ip);
142 		mhip->ip_off = off >> 3;
143 		if (off + len >= ip->ip_len-hlen)
144 			len = mhip->ip_len = ip->ip_len - hlen - off;
145 		else {
146 			mhip->ip_len = len;
147 			mhip->ip_off |= IP_MF;
148 		}
149 		mhip->ip_len += sizeof (struct ip);
150 #if vax
151 		mhip->ip_len = htons((u_short)mhip->ip_len);
152 #endif
153 		mh->m_next = m_copy(m, off, len);
154 		if (mh->m_next == 0) {
155 			(void) m_free(mh);
156 			error = ENOBUFS;	/* ??? */
157 			goto bad;
158 		}
159 #if vax
160 		mhip->ip_off = htons((u_short)mhip->ip_off);
161 #endif
162 		mhip->ip_sum = 0;
163 		mhip->ip_sum = in_cksum(mh, hlen);
164 		if (error = (*ifp->if_output)(ifp, mh, dst))
165 			break;
166 	}
167 	m_freem(m);
168 	goto done;
169 
170 unreachable:
171 	if (ipnorouteprint)
172 		printf("no route to %x (from %x, len %d)\n",
173 		    ip->ip_dst.s_addr, ip->ip_src.s_addr, ip->ip_len);
174 	error = ENETUNREACH;
175 bad:
176 	m_freem(m);
177 done:
178 	if (ro == &iproute && ro->ro_rt)
179 		RTFREE(ro->ro_rt);
180 	return (error);
181 }
182 
183 /*
184  * Copy options from ip to jp.
185  * If off is 0 all options are copied
186  * otherwise copy selectively.
187  */
188 ip_optcopy(ip, jp, off)
189 	struct ip *ip, *jp;
190 	int off;
191 {
192 	register u_char *cp, *dp;
193 	int opt, optlen, cnt;
194 
195 	cp = (u_char *)(ip + 1);
196 	dp = (u_char *)(jp + 1);
197 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
198 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
199 		opt = cp[0];
200 		if (opt == IPOPT_EOL)
201 			break;
202 		if (opt == IPOPT_NOP)
203 			optlen = 1;
204 		else
205 			optlen = cp[1];
206 		if (optlen > cnt)			/* XXX */
207 			optlen = cnt;			/* XXX */
208 		if (off == 0 || IPOPT_COPIED(opt)) {
209 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
210 			dp += optlen;
211 		}
212 	}
213 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
214 		*dp++ = IPOPT_EOL;
215 	return (optlen);
216 }
217