xref: /original-bsd/sys/netinet/ip_output.c (revision fbed46ce)
1 /*	ip_output.c	1.32	82/04/10	*/
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 #include <errno.h>
15 
16 ip_output(m, opt, ro, allowbroadcast)
17 	struct mbuf *m;
18 	struct mbuf *opt;
19 	struct route *ro;
20 	int allowbroadcast;
21 {
22 	register struct ip *ip = mtod(m, struct ip *);
23 	register struct ifnet *ifp;
24 	int len, hlen = sizeof (struct ip), off, error = 0;
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 	/*
40 	 * Find interface for this packet in the routing
41 	 * table.  Note each interface has placed itself
42 	 * in there at boot time, so calls to rtalloc
43 	 * degenerate to if_ifonnetof(ip->ip_dst.s_net).
44 	 */
45 	if (ro == 0) {
46 		ro = &iproute;
47 		bzero((caddr_t)ro, sizeof (*ro));
48 	}
49 	if (ro->ro_rt == 0) {
50 		ro->ro_dst.sa_family = AF_INET;
51 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr = ip->ip_dst;
52 		rtalloc(ro);
53 	}
54 	if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
55 		extern int ipprintfs;
56 
57 		if (ipprintfs)
58 			printf("no route to %x (from %x, len %d)\n",
59 			    ip->ip_dst.s_addr, ip->ip_src.s_addr, ip->ip_len);
60 		error = ENETUNREACH;
61 		goto bad;
62 	}
63 	dst = ro->ro_rt->rt_flags & RTF_DIRECT ?
64 	    (struct sockaddr *)&ro->ro_dst : &ro->ro_rt->rt_gateway;
65 	if (ro == &iproute)
66 		RTFREE(ro->ro_rt);
67 	if (!allowbroadcast && (ifp->if_flags & IFF_BROADCAST)) {
68 		struct sockaddr_in *sin;
69 
70 		sin = (struct sockaddr_in *)&ifp->if_broadaddr;
71 		if (sin->sin_addr.s_addr == ip->ip_dst.s_addr) {
72 			error = EPERM;		/* ??? */
73 			goto bad;
74 		}
75 	}
76 
77 	/*
78 	 * If small enough for interface, can just send directly.
79 	 */
80 	if (ip->ip_len <= ifp->if_mtu) {
81 #if vax
82 		ip->ip_len = htons((u_short)ip->ip_len);
83 		ip->ip_off = htons((u_short)ip->ip_off);
84 #endif
85 		ip->ip_sum = 0;
86 		ip->ip_sum = in_cksum(m, hlen);
87 		ro->ro_rt->rt_use++;
88 		return ((*ifp->if_output)(ifp, m, dst));
89 	}
90 
91 	/*
92 	 * Too large for interface; fragment if possible.
93 	 * Must be able to put at least 8 bytes per fragment.
94 	 */
95 	if (ip->ip_off & IP_DF) {
96 		error = EMSGSIZE;
97 		goto bad;
98 	}
99 	len = (ifp->if_mtu - hlen) &~ 7;
100 	if (len < 8) {
101 		error = EMSGSIZE;
102 		goto bad;
103 	}
104 
105 	/*
106 	 * Discard IP header from logical mbuf for m_copy's sake.
107 	 * Loop through length of segment, make a copy of each
108 	 * part and output.
109 	 */
110 	m->m_len -= sizeof (struct ip);
111 	m->m_off += sizeof (struct ip);
112 	for (off = 0; off < ip->ip_len-hlen; off += len) {
113 		struct mbuf *mh = m_get(M_DONTWAIT);
114 		struct ip *mhip;
115 
116 		if (mh == 0) {
117 			error = ENOBUFS;
118 			goto bad;
119 		}
120 		mh->m_off = MMAXOFF - hlen;
121 		mhip = mtod(mh, struct ip *);
122 		*mhip = *ip;
123 		if (hlen > sizeof (struct ip)) {
124 			int olen = ip_optcopy(ip, mhip, off);
125 			mh->m_len = sizeof (struct ip) + olen;
126 		} else
127 			mh->m_len = sizeof (struct ip);
128 		mhip->ip_off = off >> 3;
129 		if (off + len >= ip->ip_len-hlen)
130 			len = mhip->ip_len = ip->ip_len - hlen - off;
131 		else {
132 			mhip->ip_len = len;
133 			mhip->ip_off |= IP_MF;
134 		}
135 		mhip->ip_len += sizeof (struct ip);
136 #if vax
137 		mhip->ip_len = htons((u_short)mhip->ip_len);
138 #endif
139 		mh->m_next = m_copy(m, off, len);
140 		if (mh->m_next == 0) {
141 			(void) m_free(mh);
142 			error = ENOBUFS;	/* ??? */
143 			goto bad;
144 		}
145 #if vax
146 		mhip->ip_off = htons((u_short)mhip->ip_off);
147 #endif
148 		mhip->ip_sum = 0;
149 		mhip->ip_sum = in_cksum(mh, hlen);
150 		ro->ro_rt->rt_use++;
151 		if (error = (*ifp->if_output)(ifp, mh, dst))
152 			break;
153 	}
154 bad:
155 	m_freem(m);
156 	return (error);
157 }
158 
159 /*
160  * Copy options from ip to jp.
161  * If off is 0 all options are copied
162  * otherwise copy selectively.
163  */
164 ip_optcopy(ip, jp, off)
165 	struct ip *ip, *jp;
166 	int off;
167 {
168 	register u_char *cp, *dp;
169 	int opt, optlen, cnt;
170 
171 COUNT(IP_OPTCOPY);
172 	cp = (u_char *)(ip + 1);
173 	dp = (u_char *)(jp + 1);
174 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
175 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
176 		opt = cp[0];
177 		if (opt == IPOPT_EOL)
178 			break;
179 		if (opt == IPOPT_NOP)
180 			optlen = 1;
181 		else
182 			optlen = cp[1];
183 		if (optlen > cnt)			/* XXX */
184 			optlen = cnt;			/* XXX */
185 		if (off == 0 || IPOPT_COPIED(opt)) {
186 			bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
187 			dp += optlen;
188 		}
189 	}
190 	for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
191 		*dp++ = IPOPT_EOL;
192 	return (optlen);
193 }
194