xref: /csrg-svn/sys/netinet/ip_output.c (revision 6351)
1 /*	ip_output.c	1.28	82/03/29	*/
2 
3 #include "../h/param.h"
4 #include "../h/mbuf.h"
5 #include "../h/mtpr.h"
6 #include "../h/socket.h"
7 #include "../h/socketvar.h"
8 #include "../net/in.h"
9 #include "../net/in_systm.h"
10 #include "../net/if.h"
11 #include "../net/ip.h"
12 #include "../net/ip_var.h"
13 #include "../net/route.h"
14 
15 ip_output(m, opt, ro, allowbroadcast)
16 	struct mbuf *m;
17 	struct mbuf *opt;
18 	struct route *ro;
19 	int allowbroadcast;
20 {
21 	register struct ip *ip = mtod(m, struct ip *);
22 	register struct ifnet *ifp;
23 	int len, hlen = sizeof (struct ip), off, direct;
24 	struct sockaddr_in tempaddr;	/* temp kludge */
25 	struct route iproute;
26 	struct sockaddr *dst;
27 
28 COUNT(IP_OUTPUT);
29 	if (opt)				/* XXX */
30 		(void) m_free(opt);		/* XXX */
31 	/*
32 	 * Fill in IP header.
33 	 */
34 	ip->ip_v = IPVERSION;
35 	ip->ip_hl = hlen >> 2;
36 	ip->ip_off &= IP_DF;
37 	ip->ip_id = htons(ip_id++);
38 
39 #ifdef notdef
40 	/*
41 	 * Find interface for this packet in the routing
42 	 * table.  Note each interface has placed itself
43 	 * in there at boot time, so call on route degenerates
44 	 * to if_ifonnetof(ip->ip_dst.s_net).
45 	 */
46 	if (ro == 0) {
47 		ro = &iproute;
48 		bzero((caddr_t)ro, sizeof (*ro));
49 	}
50 	if (ro->ro_rt == 0) {
51 		ro->ro_dest.sin_addr = ip->ip_dst;
52 		ro->ro_dest.sin_family = AF_INET;
53 		direct = allocroute(ro);
54 	}
55 	if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0)
56 		goto bad;
57 	dst = direct ? (struct sockaddr *)&ro->ro_dest :
58 		&ro->ro_rt->rt_gateway;
59 #else
60 	/* interim kludge before routing fallout */
61 	ifp = if_ifonnetof(ip->ip_dst.s_net);
62 	if (ifp == 0)
63 		goto bad;
64 	tempaddr.sin_family = AF_INET;
65 	tempaddr.sin_addr = ip->ip_dst;
66 #endif
67 
68 	if (!allowbroadcast && (ifp->if_flags & IFF_BROADCAST)) {
69 		struct sockaddr_in *sin;
70 
71 		sin = (struct sockaddr_in *)&ifp->if_broadaddr;
72 		if (sin->sin_addr.s_addr == ip->ip_dst.s_addr)
73 			goto bad;
74 	}
75 
76 	/*
77 	 * If small enough for interface, can just send directly.
78 	 */
79 	if (ip->ip_len <= ifp->if_mtu) {
80 #if vax
81 		ip->ip_len = htons((u_short)ip->ip_len);
82 		ip->ip_off = htons((u_short)ip->ip_off);
83 #endif
84 		ip->ip_sum = 0;
85 		ip->ip_sum = in_cksum(m, hlen);
86 #ifdef notdef
87 		return ((*ifp->if_output)(ifp, m, dst));
88 #else
89 		return ((*ifp->if_output)(ifp, m,
90 			(struct sockaddr *)&tempaddr));
91 #endif
92 	}
93 
94 	/*
95 	 * Too large for interface; fragment if possible.
96 	 * Must be able to put at least 8 bytes per fragment.
97 	 */
98 	if (ip->ip_off & IP_DF)
99 		goto bad;
100 	len = (ifp->if_mtu - hlen) &~ 7;
101 	if (len < 8)
102 		goto bad;
103 
104 	/*
105 	 * Discard IP header from logical mbuf for m_copy's sake.
106 	 * Loop through length of segment, make a copy of each
107 	 * part and output.
108 	 */
109 	m->m_len -= sizeof (struct ip);
110 	m->m_off += sizeof (struct ip);
111 	for (off = 0; off < ip->ip_len-hlen; off += len) {
112 		struct mbuf *mh = m_get(M_DONTWAIT);
113 		struct ip *mhip;
114 
115 		if (mh == 0)
116 			goto bad;
117 		mh->m_off = MMAXOFF - hlen;
118 		mhip = mtod(mh, struct ip *);
119 		*mhip = *ip;
120 		if (hlen > sizeof (struct ip)) {
121 			int olen = ip_optcopy(ip, mhip, off);
122 			mh->m_len = sizeof (struct ip) + olen;
123 		} else
124 			mh->m_len = sizeof (struct ip);
125 		mhip->ip_off = off >> 3;
126 		if (off + len >= ip->ip_len-hlen)
127 			len = mhip->ip_len = ip->ip_len - hlen - off;
128 		else {
129 			mhip->ip_len = len;
130 			mhip->ip_off |= IP_MF;
131 		}
132 		mhip->ip_len += sizeof (struct ip);
133 #if vax
134 		mhip->ip_len = htons((u_short)mhip->ip_len);
135 #endif
136 		mh->m_next = m_copy(m, off, len);
137 		if (mh->m_next == 0) {
138 			(void) m_free(mh);
139 			goto bad;
140 		}
141 #if vax
142 		mhip->ip_off = htons((u_short)mhip->ip_off);
143 #endif
144 		mhip->ip_sum = 0;
145 		mhip->ip_sum = in_cksum(mh, hlen);
146 #ifdef notdef
147 		if ((*ifp->if_output)(ifp, mh, dst) == 0)
148 #else
149 		if ((*ifp->if_output)(ifp, mh,
150 		    (struct sockaddr *)&tempaddr) == 0)
151 #endif
152 			goto bad;
153 	}
154 	m_freem(m);
155 	return (1);
156 bad:
157 	m_freem(m);
158 	return (0);
159 }
160 
161 /*
162  * Copy options from ip to jp.
163  * If off is 0 all options are copied
164  * otherwise copy selectively.
165  */
166 ip_optcopy(ip, jp, off)
167 	struct ip *ip, *jp;
168 	int off;
169 {
170 	register u_char *cp, *dp;
171 	int opt, optlen, cnt;
172 
173 COUNT(IP_OPTCOPY);
174 	cp = (u_char *)(ip + 1);
175 	dp = (u_char *)(jp + 1);
176 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
177 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
178 		opt = cp[0];
179 		if (opt == IPOPT_EOL)
180 			break;
181 		if (opt == IPOPT_NOP)
182 			optlen = 1;
183 		else
184 			optlen = cp[1];
185 		if (optlen > cnt)			/* XXX */
186 			optlen = cnt;			/* XXX */
187 		if (off == 0 || IPOPT_COPIED(opt)) {
188 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
189 			dp += optlen;
190 		}
191 	}
192 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
193 		*dp++ = IPOPT_EOL;
194 	return (optlen);
195 }
196