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