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