blew it last time
[unix-history] / usr / src / sys / netinet / ip_output.c
CommitLineData
8647f264 1/* ip_output.c 1.49 83/05/27 */
6e8b2eca 2
ac904f92 3#include "../h/param.h"
dad64fdf 4#include "../h/mbuf.h"
f4d55810 5#include "../h/errno.h"
dad64fdf 6#include "../h/socket.h"
2b4b57cd 7#include "../h/socketvar.h"
f4d55810
SL
8
9#include "../net/if.h"
10#include "../net/route.h"
11
fcfe450e
BJ
12#include "../netinet/in.h"
13#include "../netinet/in_systm.h"
fcfe450e
BJ
14#include "../netinet/ip.h"
15#include "../netinet/ip_var.h"
f4d55810 16
b152d3c4
SL
17#ifdef vax
18#include "../vax/mtpr.h"
19#endif
ac904f92 20
0e3f761f 21ip_output(m, opt, ro, flags)
2b4b57cd 22 struct mbuf *m;
8a13b737 23 struct mbuf *opt;
ee787340 24 struct route *ro;
0e3f761f 25 int flags;
ac904f92 26{
2b4b57cd 27 register struct ip *ip = mtod(m, struct ip *);
8a13b737 28 register struct ifnet *ifp;
8a2f82db 29 int len, hlen = sizeof (struct ip), off, error = 0;
ee787340 30 struct route iproute;
c124e997 31 struct sockaddr *dst;
ac904f92 32
2d82c10d 33 if (opt) /* XXX */
89e6227e 34 (void) m_free(opt); /* XXX */
e8d11875 35 /*
2b4b57cd 36 * Fill in IP header.
e8d11875 37 */
2b4b57cd 38 ip->ip_hl = hlen >> 2;
0e3f761f
SL
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 }
8a13b737
BJ
44
45 /*
a13c006d 46 * Route packet.
8a13b737 47 */
ee787340
SL
48 if (ro == 0) {
49 ro = &iproute;
50 bzero((caddr_t)ro, sizeof (*ro));
51 }
a13c006d 52 dst = &ro->ro_dst;
ee787340 53 if (ro->ro_rt == 0) {
f6311fb6
SL
54 ro->ro_dst.sa_family = AF_INET;
55 ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = ip->ip_dst;
a13c006d 56 /*
0e3f761f
SL
57 * If routing to interface only,
58 * short circuit routing lookup.
a13c006d 59 */
0e3f761f 60 if (flags & IP_ROUTETOIF) {
a1edc12b 61 ifp = if_ifonnetof(in_netof(ip->ip_dst));
0e3f761f
SL
62 if (ifp == 0) {
63 error = ENETUNREACH;
64 goto bad;
65 }
a13c006d
BJ
66 goto gotif;
67 }
f6311fb6 68 rtalloc(ro);
8a13b737 69 }
0e3f761f
SL
70 if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
71 error = ENETUNREACH;
72 goto bad;
73 }
a13c006d
BJ
74 ro->ro_rt->rt_use++;
75 if (ro->ro_rt->rt_flags & RTF_GATEWAY)
76 dst = &ro->ro_rt->rt_gateway;
77gotif:
af099287 78#ifndef notdef
a13c006d
BJ
79 /*
80 * If source address not specified yet, use address
81 * of outgoing interface.
82 */
af099287 83 if (in_lnaof(ip->ip_src) == INADDR_ANY)
a13c006d
BJ
84 ip->ip_src.s_addr =
85 ((struct sockaddr_in *)&ifp->if_addr)->sin_addr.s_addr;
af099287 86#endif
8a2f82db 87
a13c006d 88 /*
af099287
SL
89 * Look for broadcast address and
90 * and verify user is allowed to send
19f96414 91 * such a packet.
a13c006d 92 */
8647f264 93 if (in_lnaof(((struct sockaddr_in *)dst)->sin_addr) == INADDR_ANY) {
19f96414
SL
94 if ((ifp->if_flags & IFF_BROADCAST) == 0) {
95 error = EADDRNOTAVAIL;
96 goto bad;
97 }
0e3f761f 98 if ((flags & IP_ALLOWBROADCAST) == 0) {
a13c006d 99 error = EACCES;
ee787340 100 goto bad;
8a2f82db 101 }
19f96414
SL
102 /* don't allow broadcast messages to be fragmented */
103 if (ip->ip_len > ifp->if_mtu) {
104 error = EMSGSIZE;
105 goto bad;
106 }
ee787340 107 }
ac904f92 108
2b4b57cd
BJ
109 /*
110 * If small enough for interface, can just send directly.
111 */
8a13b737
BJ
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);
a13c006d
BJ
117 error = (*ifp->if_output)(ifp, m, dst);
118 goto done;
cdad2eb1 119 }
2b4b57cd
BJ
120
121 /*
122 * Too large for interface; fragment if possible.
123 * Must be able to put at least 8 bytes per fragment.
124 */
8a2f82db
SL
125 if (ip->ip_off & IP_DF) {
126 error = EMSGSIZE;
2b4b57cd 127 goto bad;
8a2f82db 128 }
8a13b737 129 len = (ifp->if_mtu - hlen) &~ 7;
8a2f82db
SL
130 if (len < 8) {
131 error = EMSGSIZE;
2b4b57cd 132 goto bad;
8a2f82db 133 }
2b4b57cd
BJ
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);
0b49870f 142 for (off = 0; off < ip->ip_len-hlen; off += len) {
22bd8cdc 143 struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER);
2b4b57cd
BJ
144 struct ip *mhip;
145
8a2f82db
SL
146 if (mh == 0) {
147 error = ENOBUFS;
2b4b57cd 148 goto bad;
8a2f82db 149 }
2b4b57cd
BJ
150 mh->m_off = MMAXOFF - hlen;
151 mhip = mtod(mh, struct ip *);
152 *mhip = *ip;
4ad99bae 153 if (hlen > sizeof (struct ip)) {
2b4b57cd
BJ
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);
e8f40dc9 158 mhip->ip_off = off >> 3;
0b49870f
BJ
159 if (off + len >= ip->ip_len-hlen)
160 len = mhip->ip_len = ip->ip_len - hlen - off;
2b4b57cd
BJ
161 else {
162 mhip->ip_len = len;
163 mhip->ip_off |= IP_MF;
98444525 164 }
e8f40dc9 165 mhip->ip_len += sizeof (struct ip);
e8f40dc9 166 mhip->ip_len = htons((u_short)mhip->ip_len);
2b4b57cd
BJ
167 mh->m_next = m_copy(m, off, len);
168 if (mh->m_next == 0) {
2752c877 169 (void) m_free(mh);
8a2f82db 170 error = ENOBUFS; /* ??? */
2b4b57cd 171 goto bad;
98444525 172 }
0b49870f 173 mhip->ip_off = htons((u_short)mhip->ip_off);
0b49870f
BJ
174 mhip->ip_sum = 0;
175 mhip->ip_sum = in_cksum(mh, hlen);
8a2f82db
SL
176 if (error = (*ifp->if_output)(ifp, mh, dst))
177 break;
2b4b57cd 178 }
a13c006d
BJ
179 m_freem(m);
180 goto done;
181
2b4b57cd
BJ
182bad:
183 m_freem(m);
a13c006d 184done:
0e3f761f 185 if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
a13c006d 186 RTFREE(ro->ro_rt);
8a2f82db 187 return (error);
2b4b57cd
BJ
188}
189
190/*
191 * Copy options from ip to jp.
4ad99bae 192 * If off is 0 all options are copied
2b4b57cd
BJ
193 * otherwise copy selectively.
194 */
195ip_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)) {
4ad99bae 216 bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
2b4b57cd 217 dp += optlen;
ac904f92 218 }
e8d11875 219 }
2b4b57cd
BJ
220 for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
221 *dp++ = IPOPT_EOL;
222 return (optlen);
ac904f92 223}