new addition for portability
[unix-history] / usr / src / sys / netinet / ip_output.c
CommitLineData
5afc289d 1/*
0880b18e 2 * Copyright (c) 1982, 1986 Regents of the University of California.
5afc289d
MK
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 *
f112c673 6 * @(#)ip_output.c 7.2 (Berkeley) %G%
5afc289d 7 */
6e8b2eca 8
20666ad3
JB
9#include "param.h"
10#include "mbuf.h"
11#include "errno.h"
53ba4464 12#include "protosw.h"
20666ad3
JB
13#include "socket.h"
14#include "socketvar.h"
f4d55810
SL
15
16#include "../net/if.h"
17#include "../net/route.h"
18
20666ad3 19#include "in.h"
53ba4464 20#include "in_pcb.h"
20666ad3 21#include "in_systm.h"
dfce8240 22#include "in_var.h"
20666ad3
JB
23#include "ip.h"
24#include "ip_var.h"
f4d55810 25
b152d3c4 26#ifdef vax
f112c673 27#include "../machine/mtpr.h"
b152d3c4 28#endif
ac904f92 29
53ba4464
MK
30struct mbuf *ip_insertoptions();
31
32/*
33 * IP output. The packet in mbuf chain m contains a skeletal IP
34 * header (as ipovly). The mbuf chain containing the packet will
35 * be freed. The mbuf opt, if present, will not be freed.
36 */
0e3f761f 37ip_output(m, opt, ro, flags)
2b4b57cd 38 struct mbuf *m;
8a13b737 39 struct mbuf *opt;
ee787340 40 struct route *ro;
0e3f761f 41 int flags;
ac904f92 42{
53ba4464 43 register struct ip *ip;
8a13b737 44 register struct ifnet *ifp;
8a2f82db 45 int len, hlen = sizeof (struct ip), off, error = 0;
ee787340 46 struct route iproute;
a8671e7e 47 struct sockaddr_in *dst;
ac904f92 48
53ba4464
MK
49 if (opt)
50 m = ip_insertoptions(m, opt, &hlen);
51 ip = mtod(m, struct ip *);
e8d11875 52 /*
2b4b57cd 53 * Fill in IP header.
e8d11875 54 */
0e3f761f
SL
55 if ((flags & IP_FORWARDING) == 0) {
56 ip->ip_v = IPVERSION;
57 ip->ip_off &= IP_DF;
58 ip->ip_id = htons(ip_id++);
4adcd589 59 ip->ip_hl = hlen >> 2;
53ba4464
MK
60 } else
61 hlen = ip->ip_hl << 2;
8a13b737
BJ
62
63 /*
a13c006d 64 * Route packet.
8a13b737 65 */
ee787340
SL
66 if (ro == 0) {
67 ro = &iproute;
68 bzero((caddr_t)ro, sizeof (*ro));
69 }
a8671e7e 70 dst = (struct sockaddr_in *)&ro->ro_dst;
ccb87262
MK
71 /*
72 * If there is a cached route,
73 * check that it is to the same destination
74 * and is still up. If not, free it and try again.
75 */
76 if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
77 dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
78 RTFREE(ro->ro_rt);
79 ro->ro_rt = (struct rtentry *)0;
80 }
ee787340 81 if (ro->ro_rt == 0) {
a8671e7e
SL
82 dst->sin_family = AF_INET;
83 dst->sin_addr = ip->ip_dst;
d55475b1
MK
84 }
85 /*
86 * If routing to interface only,
87 * short circuit routing lookup.
88 */
89 if (flags & IP_ROUTETOIF) {
90 struct in_ifaddr *ia;
52c6a991 91
4a9e3e93 92 ia = (struct in_ifaddr *)ifa_ifwithdstaddr(dst);
52c6a991
MK
93 if (ia == 0)
94 ia = in_iaonnetof(in_netof(ip->ip_dst));
d55475b1
MK
95 if (ia == 0) {
96 error = ENETUNREACH;
97 goto bad;
a13c006d 98 }
d55475b1
MK
99 ifp = ia->ia_ifp;
100 } else {
d55475b1
MK
101 if (ro->ro_rt == 0)
102 rtalloc(ro);
103 if (ro->ro_rt == 0 || (ifp = ro->ro_rt->rt_ifp) == 0) {
104 error = ENETUNREACH;
105 goto bad;
106 }
107 ro->ro_rt->rt_use++;
108 if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
109 dst = (struct sockaddr_in *)&ro->ro_rt->rt_gateway;
0e3f761f 110 }
5afc289d
MK
111#ifndef notdef
112 /*
113 * If source address not specified yet, use address
114 * of outgoing interface.
115 */
116 if (ip->ip_src.s_addr == INADDR_ANY) {
117 register struct in_ifaddr *ia;
118
119 for (ia = in_ifaddr; ia; ia = ia->ia_next)
120 if (ia->ia_ifp == ifp) {
121 ip->ip_src = IA_SIN(ia)->sin_addr;
122 break;
123 }
124 }
125#endif
a13c006d 126 /*
af099287
SL
127 * Look for broadcast address and
128 * and verify user is allowed to send
19f96414 129 * such a packet.
a13c006d 130 */
dfce8240 131 if (in_broadcast(dst->sin_addr)) {
19f96414
SL
132 if ((ifp->if_flags & IFF_BROADCAST) == 0) {
133 error = EADDRNOTAVAIL;
134 goto bad;
135 }
0e3f761f 136 if ((flags & IP_ALLOWBROADCAST) == 0) {
a13c006d 137 error = EACCES;
ee787340 138 goto bad;
8a2f82db 139 }
19f96414
SL
140 /* don't allow broadcast messages to be fragmented */
141 if (ip->ip_len > ifp->if_mtu) {
142 error = EMSGSIZE;
143 goto bad;
144 }
ee787340 145 }
ac904f92 146
2b4b57cd
BJ
147 /*
148 * If small enough for interface, can just send directly.
149 */
8a13b737
BJ
150 if (ip->ip_len <= ifp->if_mtu) {
151 ip->ip_len = htons((u_short)ip->ip_len);
152 ip->ip_off = htons((u_short)ip->ip_off);
153 ip->ip_sum = 0;
154 ip->ip_sum = in_cksum(m, hlen);
a8671e7e 155 error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst);
a13c006d 156 goto done;
cdad2eb1 157 }
2b4b57cd
BJ
158
159 /*
160 * Too large for interface; fragment if possible.
161 * Must be able to put at least 8 bytes per fragment.
162 */
8a2f82db
SL
163 if (ip->ip_off & IP_DF) {
164 error = EMSGSIZE;
2b4b57cd 165 goto bad;
8a2f82db 166 }
8a13b737 167 len = (ifp->if_mtu - hlen) &~ 7;
8a2f82db
SL
168 if (len < 8) {
169 error = EMSGSIZE;
2b4b57cd 170 goto bad;
8a2f82db 171 }
2b4b57cd
BJ
172
173 /*
174 * Discard IP header from logical mbuf for m_copy's sake.
175 * Loop through length of segment, make a copy of each
176 * part and output.
177 */
178 m->m_len -= sizeof (struct ip);
179 m->m_off += sizeof (struct ip);
0b49870f 180 for (off = 0; off < ip->ip_len-hlen; off += len) {
22bd8cdc 181 struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER);
2b4b57cd
BJ
182 struct ip *mhip;
183
8a2f82db
SL
184 if (mh == 0) {
185 error = ENOBUFS;
2b4b57cd 186 goto bad;
8a2f82db 187 }
2b4b57cd
BJ
188 mh->m_off = MMAXOFF - hlen;
189 mhip = mtod(mh, struct ip *);
190 *mhip = *ip;
4ad99bae 191 if (hlen > sizeof (struct ip)) {
2b4b57cd
BJ
192 int olen = ip_optcopy(ip, mhip, off);
193 mh->m_len = sizeof (struct ip) + olen;
194 } else
195 mh->m_len = sizeof (struct ip);
2e7801e0 196 mhip->ip_off = (off >> 3) + (ip->ip_off & ~IP_MF);
4adcd589
MK
197 if (ip->ip_off & IP_MF)
198 mhip->ip_off |= IP_MF;
0b49870f
BJ
199 if (off + len >= ip->ip_len-hlen)
200 len = mhip->ip_len = ip->ip_len - hlen - off;
2b4b57cd
BJ
201 else {
202 mhip->ip_len = len;
203 mhip->ip_off |= IP_MF;
98444525 204 }
e8f40dc9 205 mhip->ip_len += sizeof (struct ip);
e8f40dc9 206 mhip->ip_len = htons((u_short)mhip->ip_len);
2b4b57cd
BJ
207 mh->m_next = m_copy(m, off, len);
208 if (mh->m_next == 0) {
2752c877 209 (void) m_free(mh);
8a2f82db 210 error = ENOBUFS; /* ??? */
2b4b57cd 211 goto bad;
98444525 212 }
0b49870f 213 mhip->ip_off = htons((u_short)mhip->ip_off);
0b49870f
BJ
214 mhip->ip_sum = 0;
215 mhip->ip_sum = in_cksum(mh, hlen);
a8671e7e 216 if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst))
8a2f82db 217 break;
2b4b57cd
BJ
218 }
219bad:
220 m_freem(m);
a13c006d 221done:
0e3f761f 222 if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
a13c006d 223 RTFREE(ro->ro_rt);
8a2f82db 224 return (error);
2b4b57cd
BJ
225}
226
53ba4464
MK
227/*
228 * Insert IP options into preformed packet.
229 * Adjust IP destination as required for IP source routing,
230 * as indicated by a non-zero in_addr at the start of the options.
231 */
232struct mbuf *
233ip_insertoptions(m, opt, phlen)
234 register struct mbuf *m;
235 struct mbuf *opt;
236 int *phlen;
237{
238 register struct ipoption *p = mtod(opt, struct ipoption *);
239 struct mbuf *n;
240 register struct ip *ip = mtod(m, struct ip *);
8011f5df 241 unsigned optlen;
53ba4464
MK
242
243 optlen = opt->m_len - sizeof(p->ipopt_dst);
244 if (p->ipopt_dst.s_addr)
245 ip->ip_dst = p->ipopt_dst;
246 if (m->m_off >= MMAXOFF || MMINOFF + optlen > m->m_off) {
247 MGET(n, M_DONTWAIT, MT_HEADER);
248 if (n == 0)
249 return (m);
250 m->m_len -= sizeof(struct ip);
251 m->m_off += sizeof(struct ip);
252 n->m_next = m;
253 m = n;
254 m->m_off = MMAXOFF - sizeof(struct ip) - optlen;
255 m->m_len = optlen + sizeof(struct ip);
256 bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
257 } else {
258 m->m_off -= optlen;
259 m->m_len += optlen;
260 ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
261 }
262 ip = mtod(m, struct ip *);
8011f5df 263 bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);
53ba4464
MK
264 *phlen = sizeof(struct ip) + optlen;
265 ip->ip_len += optlen;
266 return (m);
267}
268
2b4b57cd
BJ
269/*
270 * Copy options from ip to jp.
4ad99bae 271 * If off is 0 all options are copied
2b4b57cd
BJ
272 * otherwise copy selectively.
273 */
274ip_optcopy(ip, jp, off)
275 struct ip *ip, *jp;
276 int off;
277{
278 register u_char *cp, *dp;
279 int opt, optlen, cnt;
280
281 cp = (u_char *)(ip + 1);
282 dp = (u_char *)(jp + 1);
283 cnt = (ip->ip_hl << 2) - sizeof (struct ip);
284 for (; cnt > 0; cnt -= optlen, cp += optlen) {
285 opt = cp[0];
286 if (opt == IPOPT_EOL)
287 break;
288 if (opt == IPOPT_NOP)
289 optlen = 1;
290 else
53ba4464 291 optlen = cp[IPOPT_OLEN];
2b4b57cd
BJ
292 if (optlen > cnt) /* XXX */
293 optlen = cnt; /* XXX */
294 if (off == 0 || IPOPT_COPIED(opt)) {
4ad99bae 295 bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
2b4b57cd 296 dp += optlen;
ac904f92 297 }
e8d11875 298 }
2b4b57cd
BJ
299 for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
300 *dp++ = IPOPT_EOL;
301 return (optlen);
ac904f92 302}
53ba4464
MK
303
304/*
305 * IP socket option processing.
306 */
307ip_ctloutput(op, so, level, optname, m)
308 int op;
309 struct socket *so;
310 int level, optname;
311 struct mbuf **m;
312{
313 int error = 0;
314 struct inpcb *inp = sotoinpcb(so);
315
316 if (level != IPPROTO_IP)
317 error = EINVAL;
318 else switch (op) {
319
320 case PRCO_SETOPT:
321 switch (optname) {
322 case IP_OPTIONS:
0c3fb1b4 323 return (ip_pcbopts(&inp->inp_options, *m));
53ba4464
MK
324
325 default:
326 error = EINVAL;
327 break;
328 }
329 break;
330
331 case PRCO_GETOPT:
332 switch (optname) {
333 case IP_OPTIONS:
334 *m = m_get(M_WAIT, MT_SOOPTS);
335 if (inp->inp_options) {
336 (*m)->m_off = inp->inp_options->m_off;
337 (*m)->m_len = inp->inp_options->m_len;
338 bcopy(mtod(inp->inp_options, caddr_t),
8011f5df 339 mtod(*m, caddr_t), (unsigned)(*m)->m_len);
53ba4464
MK
340 } else
341 (*m)->m_len = 0;
342 break;
343 default:
344 error = EINVAL;
345 break;
346 }
347 break;
348 }
349 if (op == PRCO_SETOPT)
8011f5df 350 (void)m_free(*m);
53ba4464
MK
351 return (error);
352}
353
354/*
0c3fb1b4
MK
355 * Set up IP options in pcb for insertion in output packets.
356 * Store in mbuf with pointer in pcbopt, adding pseudo-option
357 * with destination address if source routed.
53ba4464 358 */
0c3fb1b4
MK
359ip_pcbopts(pcbopt, m)
360 struct mbuf **pcbopt;
361 register struct mbuf *m;
53ba4464
MK
362{
363 register cnt, optlen;
364 register u_char *cp;
365 u_char opt;
366
367 /* turn off any old options */
0c3fb1b4 368 if (*pcbopt)
8011f5df 369 (void)m_free(*pcbopt);
0c3fb1b4 370 *pcbopt = 0;
53ba4464
MK
371 if (m == (struct mbuf *)0 || m->m_len == 0) {
372 /*
373 * Only turning off any previous options.
374 */
375 if (m)
8011f5df 376 (void)m_free(m);
53ba4464
MK
377 return (0);
378 }
379
380#ifndef vax
381 if (m->m_len % sizeof(long))
382 goto bad;
383#endif
384 /*
385 * IP first-hop destination address will be stored before
386 * actual options; move other options back
387 * and clear it when none present.
388 */
389#if MAX_IPOPTLEN >= MMAXOFF - MMINOFF
390 if (m->m_off + m->m_len + sizeof(struct in_addr) > MAX_IPOPTLEN)
391 goto bad;
392#else
393 if (m->m_off + m->m_len + sizeof(struct in_addr) > MMAXOFF)
394 goto bad;
395#endif
396 cnt = m->m_len;
397 m->m_len += sizeof(struct in_addr);
398 cp = mtod(m, u_char *) + sizeof(struct in_addr);
8011f5df 399 ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt);
53ba4464
MK
400 bzero(mtod(m, caddr_t), sizeof(struct in_addr));
401
402 for (; cnt > 0; cnt -= optlen, cp += optlen) {
403 opt = cp[IPOPT_OPTVAL];
404 if (opt == IPOPT_EOL)
405 break;
406 if (opt == IPOPT_NOP)
407 optlen = 1;
408 else {
409 optlen = cp[IPOPT_OLEN];
410 if (optlen <= IPOPT_OLEN || optlen > cnt)
411 goto bad;
412 }
413 switch (opt) {
414
415 default:
416 break;
417
418 case IPOPT_LSRR:
419 case IPOPT_SSRR:
420 /*
421 * user process specifies route as:
422 * ->A->B->C->D
423 * D must be our final destination (but we can't
424 * check that since we may not have connected yet).
425 * A is first hop destination, which doesn't appear in
426 * actual IP option, but is stored before the options.
427 */
428 if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr))
429 goto bad;
430 m->m_len -= sizeof(struct in_addr);
431 cnt -= sizeof(struct in_addr);
432 optlen -= sizeof(struct in_addr);
433 cp[IPOPT_OLEN] = optlen;
434 /*
435 * Move first hop before start of options.
436 */
8011f5df 437 bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t),
53ba4464
MK
438 sizeof(struct in_addr));
439 /*
440 * Then copy rest of options back
441 * to close up the deleted entry.
442 */
8011f5df
MK
443 ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] +
444 sizeof(struct in_addr)),
445 (caddr_t)&cp[IPOPT_OFFSET+1],
446 (unsigned)cnt + sizeof(struct in_addr));
53ba4464
MK
447 break;
448 }
449 }
0c3fb1b4 450 *pcbopt = m;
53ba4464
MK
451 return (0);
452
453bad:
8011f5df 454 (void)m_free(m);
53ba4464
MK
455 return (EINVAL);
456}