localize header files
[unix-history] / usr / src / sys / netinet / ip_input.c
CommitLineData
fcfe450e 1/* ip_input.c 1.51 82/10/09 */
6e8b2eca 2
e1d82856 3#include "../h/param.h"
d10bd5b7 4#include "../h/systm.h"
e6dd2097 5#include "../h/mbuf.h"
eb44bfb2 6#include "../h/protosw.h"
2b4b57cd 7#include "../h/socket.h"
fcfe450e
BJ
8#include "../netinet/in.h"
9#include "../netinet/in_systm.h"
4ad99bae 10#include "../net/if.h"
fcfe450e
BJ
11#include "../netinet/ip.h" /* belongs before in.h */
12#include "../netinet/ip_var.h"
13#include "../netinet/ip_icmp.h"
14#include "../netinet/tcp.h"
b2ac7f3b
BJ
15#include <time.h>
16#include "../h/kernel.h"
72e4f44e 17#include <errno.h>
e6dd2097 18
eb44bfb2 19u_char ip_protox[IPPROTO_MAX];
1e977657 20int ipqmaxlen = IFQ_MAXLEN;
ee787340 21struct ifnet *ifinet; /* first inet interface */
eb44bfb2 22
d52566dd 23/*
b454c3ea 24 * IP initialization: fill in IP protocol switch table.
405c9168 25 * All protocols not implemented in kernel go to raw IP protocol handler.
d52566dd
BJ
26 */
27ip_init()
28{
eb44bfb2
BJ
29 register struct protosw *pr;
30 register int i;
eb44bfb2
BJ
31
32 pr = pffindproto(PF_INET, IPPROTO_RAW);
33 if (pr == 0)
34 panic("ip_init");
35 for (i = 0; i < IPPROTO_MAX; i++)
36 ip_protox[i] = pr - protosw;
37 for (pr = protosw; pr <= protoswLAST; pr++)
38 if (pr->pr_family == PF_INET &&
39 pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
40 ip_protox[pr->pr_protocol] = pr - protosw;
d52566dd 41 ipq.next = ipq.prev = &ipq;
b2ac7f3b 42 ip_id = time.tv_sec & 0xffff;
1e977657 43 ipintrq.ifq_maxlen = ipqmaxlen;
ee787340 44 ifinet = if_ifwithaf(AF_INET);
d52566dd
BJ
45}
46
eb44bfb2 47u_char ipcksum = 1;
e6dd2097 48struct ip *ip_reass();
ee787340 49struct sockaddr_in ipaddr = { AF_INET };
e6dd2097 50
e6dd2097
BJ
51/*
52 * Ip input routine. Checksum and byte swap header. If fragmented
53 * try to reassamble. If complete and fragment queue exists, discard.
54 * Process options. Pass to next level.
55 */
8a13b737 56ipintr()
e1d82856 57{
2b4b57cd 58 register struct ip *ip;
8a13b737 59 register struct mbuf *m;
7c08c626 60 struct mbuf *m0, *mopt;
e6dd2097 61 register int i;
e1d82856 62 register struct ipq *fp;
8a13b737 63 int hlen, s;
e1d82856 64
8a13b737 65next:
e6dd2097 66 /*
8a13b737
BJ
67 * Get next datagram off input queue and get IP header
68 * in first mbuf.
e6dd2097 69 */
8a13b737
BJ
70 s = splimp();
71 IF_DEQUEUE(&ipintrq, m);
72 splx(s);
7ac98d3c 73 if (m == 0)
8a13b737 74 return;
9411b6be
BJ
75 if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct ip)) &&
76 (m = m_pullup(m, sizeof (struct ip))) == 0)
77 return;
e6dd2097 78 ip = mtod(m, struct ip *);
405c9168 79 if ((hlen = ip->ip_hl << 2) > m->m_len) {
9411b6be
BJ
80 if ((m = m_pullup(m, hlen)) == 0)
81 return;
405c9168
BJ
82 ip = mtod(m, struct ip *);
83 }
4ad99bae 84 if (ipcksum)
7c08c626 85 if (ip->ip_sum = in_cksum(m, hlen)) {
405c9168 86 printf("ip_sum %x\n", ip->ip_sum); /* XXX */
4ad99bae
BJ
87 ipstat.ips_badsum++;
88 goto bad;
e1d82856 89 }
4ad99bae 90
7c08c626 91#if vax
4ad99bae
BJ
92 /*
93 * Convert fields to host representation.
94 */
cdad2eb1 95 ip->ip_len = ntohs((u_short)ip->ip_len);
e6dd2097 96 ip->ip_id = ntohs(ip->ip_id);
4ad99bae 97 ip->ip_off = ntohs((u_short)ip->ip_off);
7c08c626 98#endif
e1d82856 99
d10bd5b7 100 /*
e6dd2097
BJ
101 * Check that the amount of data in the buffers
102 * is as at least much as the IP header would have us expect.
103 * Trim mbufs if longer than we expect.
104 * Drop packet if shorter than we expect.
d10bd5b7 105 */
9c0ca361 106 i = -ip->ip_len;
405c9168 107 m0 = m;
9c0ca361 108 for (;;) {
e1d82856 109 i += m->m_len;
9c0ca361
BJ
110 if (m->m_next == 0)
111 break;
112 m = m->m_next;
1dd55890 113 }
9c0ca361
BJ
114 if (i != 0) {
115 if (i < 0) {
405c9168 116 ipstat.ips_tooshort++;
4ad99bae 117 goto bad;
405c9168 118 }
9c0ca361
BJ
119 if (i <= m->m_len)
120 m->m_len -= i;
121 else
122 m_adj(m0, -i);
d10bd5b7 123 }
9c0ca361 124 m = m0;
e1d82856 125
e6dd2097
BJ
126 /*
127 * Process options and, if not destined for us,
72e4f44e
SL
128 * ship it on. ip_dooptions returns 1 when an
129 * error was detected (causing an icmp message
130 * to be sent).
e6dd2097 131 */
72e4f44e
SL
132 if (hlen > sizeof (struct ip) && ip_dooptions(ip))
133 goto next;
ee787340
SL
134
135 /*
c124e997
SL
136 * Fast check on the first internet
137 * interface in the list.
ee787340
SL
138 */
139 if (ifinet) {
140 struct sockaddr_in *sin;
141
142 sin = (struct sockaddr_in *)&ifinet->if_addr;
143 if (sin->sin_addr.s_addr == ip->ip_dst.s_addr)
144 goto ours;
cdff57cc 145 sin = (struct sockaddr_in *)&ifinet->if_broadaddr;
c124e997
SL
146 if ((ifinet->if_flags & IFF_BROADCAST) &&
147 sin->sin_addr.s_addr == ip->ip_dst.s_addr)
148 goto ours;
ee787340
SL
149 }
150 ipaddr.sin_addr = ip->ip_dst;
151 if (if_ifwithaddr((struct sockaddr *)&ipaddr) == 0) {
72e4f44e 152 ip_forward(ip);
8a13b737 153 goto next;
d10bd5b7 154 }
e1d82856 155
ee787340 156ours:
e6dd2097
BJ
157 /*
158 * Look for queue of fragments
159 * of this datagram.
160 */
161 for (fp = ipq.next; fp != &ipq; fp = fp->next)
162 if (ip->ip_id == fp->ipq_id &&
163 ip->ip_src.s_addr == fp->ipq_src.s_addr &&
164 ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
165 ip->ip_p == fp->ipq_p)
166 goto found;
167 fp = 0;
168found:
e1d82856 169
e6dd2097
BJ
170 /*
171 * Adjust ip_len to not reflect header,
172 * set ip_mff if more fragments are expected,
173 * convert offset of this to bytes.
174 */
175 ip->ip_len -= hlen;
eb44bfb2 176 ((struct ipasfrag *)ip)->ipf_mff = 0;
e6dd2097 177 if (ip->ip_off & IP_MF)
eb44bfb2 178 ((struct ipasfrag *)ip)->ipf_mff = 1;
e6dd2097 179 ip->ip_off <<= 3;
e1d82856 180
e6dd2097
BJ
181 /*
182 * If datagram marked as having more fragments
183 * or if this is not the first fragment,
184 * attempt reassembly; if it succeeds, proceed.
185 */
eb44bfb2
BJ
186 if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) {
187 ip = ip_reass((struct ipasfrag *)ip, fp);
e6dd2097 188 if (ip == 0)
8a13b737 189 goto next;
e6dd2097
BJ
190 hlen = ip->ip_hl << 2;
191 m = dtom(ip);
192 } else
193 if (fp)
194 (void) ip_freef(fp);
4ad99bae
BJ
195
196 /*
197 * Switch out to protocol's input routine.
198 */
eb44bfb2 199 (*protosw[ip_protox[ip->ip_p]].pr_input)(m);
8a13b737 200 goto next;
4ad99bae
BJ
201bad:
202 m_freem(m);
8a13b737 203 goto next;
e6dd2097 204}
e1d82856 205
e6dd2097
BJ
206/*
207 * Take incoming datagram fragment and try to
4ad99bae 208 * reassemble it into whole datagram. If a chain for
e6dd2097
BJ
209 * reassembly of this datagram already exists, then it
210 * is given as fp; otherwise have to make a chain.
211 */
212struct ip *
213ip_reass(ip, fp)
eb44bfb2 214 register struct ipasfrag *ip;
e6dd2097
BJ
215 register struct ipq *fp;
216{
217 register struct mbuf *m = dtom(ip);
eb44bfb2 218 register struct ipasfrag *q;
e6dd2097
BJ
219 struct mbuf *t;
220 int hlen = ip->ip_hl << 2;
221 int i, next;
d10bd5b7 222
e6dd2097
BJ
223 /*
224 * Presence of header sizes in mbufs
225 * would confuse code below.
226 */
227 m->m_off += hlen;
228 m->m_len -= hlen;
d10bd5b7 229
e6dd2097
BJ
230 /*
231 * If first fragment to arrive, create a reassembly queue.
232 */
233 if (fp == 0) {
e6b33a03 234 if ((t = m_get(M_WAIT)) == NULL)
e6dd2097 235 goto dropfrag;
e6dd2097
BJ
236 fp = mtod(t, struct ipq *);
237 insque(fp, &ipq);
238 fp->ipq_ttl = IPFRAGTTL;
239 fp->ipq_p = ip->ip_p;
240 fp->ipq_id = ip->ip_id;
eb44bfb2
BJ
241 fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
242 fp->ipq_src = ((struct ip *)ip)->ip_src;
243 fp->ipq_dst = ((struct ip *)ip)->ip_dst;
405c9168
BJ
244 q = (struct ipasfrag *)fp;
245 goto insert;
e6dd2097 246 }
e1d82856 247
e6dd2097
BJ
248 /*
249 * Find a segment which begins after this one does.
250 */
eb44bfb2 251 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
e6dd2097
BJ
252 if (q->ip_off > ip->ip_off)
253 break;
e1d82856 254
e6dd2097
BJ
255 /*
256 * If there is a preceding segment, it may provide some of
257 * our data already. If so, drop the data from the incoming
258 * segment. If it provides all of our data, drop us.
259 */
eb44bfb2
BJ
260 if (q->ipf_prev != (struct ipasfrag *)fp) {
261 i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
e6dd2097
BJ
262 if (i > 0) {
263 if (i >= ip->ip_len)
264 goto dropfrag;
265 m_adj(dtom(ip), i);
266 ip->ip_off += i;
267 ip->ip_len -= i;
e1d82856 268 }
d10bd5b7 269 }
e1d82856 270
e6dd2097
BJ
271 /*
272 * While we overlap succeeding segments trim them or,
273 * if they are completely covered, dequeue them.
274 */
eb44bfb2 275 while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
e6dd2097
BJ
276 i = (ip->ip_off + ip->ip_len) - q->ip_off;
277 if (i < q->ip_len) {
278 q->ip_len -= i;
c107df34 279 q->ip_off += i;
e6dd2097
BJ
280 m_adj(dtom(q), i);
281 break;
282 }
eb44bfb2
BJ
283 q = q->ipf_next;
284 m_freem(dtom(q->ipf_prev));
285 ip_deq(q->ipf_prev);
e6dd2097 286 }
e1d82856 287
405c9168 288insert:
e6dd2097
BJ
289 /*
290 * Stick new segment in its place;
291 * check for complete reassembly.
292 */
eb44bfb2 293 ip_enq(ip, q->ipf_prev);
e6dd2097 294 next = 0;
eb44bfb2 295 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
e6dd2097
BJ
296 if (q->ip_off != next)
297 return (0);
298 next += q->ip_len;
299 }
eb44bfb2 300 if (q->ipf_prev->ipf_mff)
e6dd2097 301 return (0);
e1d82856 302
e6dd2097
BJ
303 /*
304 * Reassembly is complete; concatenate fragments.
305 */
306 q = fp->ipq_next;
307 m = dtom(q);
308 t = m->m_next;
309 m->m_next = 0;
310 m_cat(m, t);
dfb346d0
BJ
311 q = q->ipf_next;
312 while (q != (struct ipasfrag *)fp) {
313 t = dtom(q);
314 q = q->ipf_next;
315 m_cat(m, t);
316 }
e1d82856 317
e6dd2097
BJ
318 /*
319 * Create header for new ip packet by
320 * modifying header of first packet;
321 * dequeue and discard fragment reassembly header.
322 * Make header visible.
323 */
324 ip = fp->ipq_next;
325 ip->ip_len = next;
eb44bfb2
BJ
326 ((struct ip *)ip)->ip_src = fp->ipq_src;
327 ((struct ip *)ip)->ip_dst = fp->ipq_dst;
e6dd2097 328 remque(fp);
cdad2eb1 329 (void) m_free(dtom(fp));
e6dd2097 330 m = dtom(ip);
eb44bfb2
BJ
331 m->m_len += sizeof (struct ipasfrag);
332 m->m_off -= sizeof (struct ipasfrag);
333 return ((struct ip *)ip);
e6dd2097
BJ
334
335dropfrag:
336 m_freem(m);
337 return (0);
e1d82856
BJ
338}
339
e6dd2097
BJ
340/*
341 * Free a fragment reassembly header and all
342 * associated datagrams.
343 */
344struct ipq *
345ip_freef(fp)
346 struct ipq *fp;
e1d82856 347{
eb44bfb2 348 register struct ipasfrag *q;
e6dd2097
BJ
349 struct mbuf *m;
350
eb44bfb2 351 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
e6dd2097
BJ
352 m_freem(dtom(q));
353 m = dtom(fp);
354 fp = fp->next;
355 remque(fp->prev);
cdad2eb1 356 (void) m_free(m);
e6dd2097 357 return (fp);
e1d82856
BJ
358}
359
e6dd2097
BJ
360/*
361 * Put an ip fragment on a reassembly chain.
362 * Like insque, but pointers in middle of structure.
363 */
364ip_enq(p, prev)
eb44bfb2 365 register struct ipasfrag *p, *prev;
e1d82856 366{
e1d82856 367
eb44bfb2
BJ
368 p->ipf_prev = prev;
369 p->ipf_next = prev->ipf_next;
370 prev->ipf_next->ipf_prev = p;
371 prev->ipf_next = p;
e1d82856
BJ
372}
373
e6dd2097
BJ
374/*
375 * To ip_enq as remque is to insque.
376 */
377ip_deq(p)
eb44bfb2 378 register struct ipasfrag *p;
e1d82856 379{
e6dd2097 380
eb44bfb2
BJ
381 p->ipf_prev->ipf_next = p->ipf_next;
382 p->ipf_next->ipf_prev = p->ipf_prev;
e1d82856
BJ
383}
384
e6dd2097
BJ
385/*
386 * IP timer processing;
387 * if a timer expires on a reassembly
388 * queue, discard it.
389 */
d52566dd 390ip_slowtimo()
e1d82856
BJ
391{
392 register struct ipq *fp;
e6dd2097 393 int s = splnet();
e1d82856 394
4aed14e3
BJ
395 fp = ipq.next;
396 if (fp == 0) {
397 splx(s);
398 return;
399 }
400 while (fp != &ipq)
e6dd2097
BJ
401 if (--fp->ipq_ttl == 0)
402 fp = ip_freef(fp);
403 else
404 fp = fp->next;
e6dd2097 405 splx(s);
e1d82856
BJ
406}
407
4ad99bae
BJ
408/*
409 * Drain off all datagram fragments.
410 */
d52566dd
BJ
411ip_drain()
412{
413
4ad99bae
BJ
414 while (ipq.next != &ipq)
415 (void) ip_freef(ipq.next);
d52566dd 416}
2b4b57cd 417
e6dd2097
BJ
418/*
419 * Do option processing on a datagram,
420 * possibly discarding it if bad options
421 * are encountered.
422 */
423ip_dooptions(ip)
424 struct ip *ip;
e1d82856 425{
e6dd2097 426 register u_char *cp;
72e4f44e 427 int opt, optlen, cnt, code, type;
2b4b57cd 428 struct in_addr *sin;
d52566dd 429 register struct ip_timestamp *ipt;
4ad99bae
BJ
430 register struct ifnet *ifp;
431 struct in_addr t;
e6dd2097
BJ
432
433 cp = (u_char *)(ip + 1);
434 cnt = (ip->ip_hl << 2) - sizeof (struct ip);
435 for (; cnt > 0; cnt -= optlen, cp += optlen) {
436 opt = cp[0];
437 if (opt == IPOPT_EOL)
438 break;
439 if (opt == IPOPT_NOP)
440 optlen = 1;
441 else
442 optlen = cp[1];
443 switch (opt) {
e1d82856 444
e6dd2097
BJ
445 default:
446 break;
e1d82856 447
4ad99bae
BJ
448 /*
449 * Source routing with record.
450 * Find interface with current destination address.
451 * If none on this machine then drop if strictly routed,
452 * or do nothing if loosely routed.
453 * Record interface address and bring up next address
454 * component. If strictly routed make sure next
455 * address on directly accessible net.
456 */
e6dd2097 457 case IPOPT_LSRR:
a71ece0a 458 case IPOPT_SSRR:
d52566dd 459 if (cp[2] < 4 || cp[2] > optlen - (sizeof (long) - 1))
e6dd2097 460 break;
2b4b57cd 461 sin = (struct in_addr *)(cp + cp[2]);
ee787340
SL
462 ipaddr.sin_addr = *sin;
463 ifp = if_ifwithaddr((struct sockaddr *)&ipaddr);
72e4f44e 464 type = ICMP_UNREACH, code = ICMP_UNREACH_SRCFAIL;
4ad99bae
BJ
465 if (ifp == 0) {
466 if (opt == IPOPT_SSRR)
467 goto bad;
468 break;
e6dd2097 469 }
4ad99bae
BJ
470 t = ip->ip_dst; ip->ip_dst = *sin; *sin = t;
471 cp[2] += 4;
472 if (cp[2] > optlen - (sizeof (long) - 1))
473 break;
474 ip->ip_dst = sin[1];
ee787340 475 if (opt == IPOPT_SSRR &&
6187f8f4 476 if_ifonnetof(in_netof(ip->ip_dst)) == 0)
4ad99bae 477 goto bad;
e6dd2097
BJ
478 break;
479
480 case IPOPT_TS:
72e4f44e
SL
481 code = cp - (u_char *)ip;
482 type = ICMP_PARAMPROB;
d52566dd
BJ
483 ipt = (struct ip_timestamp *)cp;
484 if (ipt->ipt_len < 5)
e6dd2097 485 goto bad;
d52566dd
BJ
486 if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
487 if (++ipt->ipt_oflw == 0)
e6dd2097 488 goto bad;
e6dd2097
BJ
489 break;
490 }
2b4b57cd 491 sin = (struct in_addr *)(cp+cp[2]);
d52566dd 492 switch (ipt->ipt_flg) {
e1d82856 493
e6dd2097
BJ
494 case IPOPT_TS_TSONLY:
495 break;
e1d82856 496
e6dd2097 497 case IPOPT_TS_TSANDADDR:
d52566dd 498 if (ipt->ipt_ptr + 8 > ipt->ipt_len)
e6dd2097 499 goto bad;
ee787340
SL
500 if (ifinet == 0)
501 goto bad; /* ??? */
502 *sin++ = ((struct sockaddr_in *)&ifinet->if_addr)->sin_addr;
e6dd2097
BJ
503 break;
504
505 case IPOPT_TS_PRESPEC:
ee787340 506 ipaddr.sin_addr = *sin;
72e4f44e 507 if (!if_ifwithaddr((struct sockaddr *)&ipaddr))
4ad99bae 508 continue;
d52566dd 509 if (ipt->ipt_ptr + 8 > ipt->ipt_len)
e6dd2097 510 goto bad;
d52566dd 511 ipt->ipt_ptr += 4;
e1d82856
BJ
512 break;
513
514 default:
e6dd2097 515 goto bad;
e1d82856 516 }
2b4b57cd 517 *(n_time *)sin = iptime();
d52566dd 518 ipt->ipt_ptr += 4;
e6dd2097 519 }
e1d82856 520 }
72e4f44e 521 return (0);
e6dd2097 522bad:
72e4f44e
SL
523 icmp_error(ip, type, code);
524 return (1);
e1d82856
BJ
525}
526
e6dd2097 527/*
4ad99bae
BJ
528 * Strip out IP options, at higher
529 * level protocol in the kernel.
530 * Second argument is buffer to which options
531 * will be moved, and return value is their length.
e6dd2097 532 */
7c08c626 533ip_stripoptions(ip, mopt)
e6dd2097 534 struct ip *ip;
7c08c626 535 struct mbuf *mopt;
e1d82856 536{
e6dd2097
BJ
537 register int i;
538 register struct mbuf *m;
e6dd2097 539 int olen;
e6dd2097
BJ
540
541 olen = (ip->ip_hl<<2) - sizeof (struct ip);
4ad99bae
BJ
542 m = dtom(ip);
543 ip++;
7c08c626
BJ
544 if (mopt) {
545 mopt->m_len = olen;
546 mopt->m_off = MMINOFF;
547 bcopy((caddr_t)ip, mtod(m, caddr_t), (unsigned)olen);
548 }
e6dd2097 549 i = m->m_len - (sizeof (struct ip) + olen);
cdad2eb1 550 bcopy((caddr_t)ip+olen, (caddr_t)ip, (unsigned)i);
4aed14e3 551 m->m_len -= olen;
e1d82856 552}
72e4f44e 553
39674d5f 554u_char inetctlerrmap[] = {
72e4f44e 555 ECONNABORTED, ECONNABORTED, 0, 0,
67387c9c
SL
556 0, 0,
557 EHOSTDOWN, EHOSTUNREACH, ENETUNREACH, EHOSTUNREACH,
72e4f44e
SL
558 ECONNREFUSED, ECONNREFUSED, EMSGSIZE, 0,
559 0, 0, 0, 0
560};
561
562ip_ctlinput(cmd, arg)
563 int cmd;
564 caddr_t arg;
565{
566 struct in_addr *sin;
39674d5f 567 int tcp_abort(), udp_abort();
72e4f44e
SL
568 extern struct inpcb tcb, udb;
569
570 if (cmd < 0 || cmd > PRC_NCMDS)
571 return;
39674d5f 572 if (inetctlerrmap[cmd] == 0)
72e4f44e
SL
573 return; /* XXX */
574 if (cmd == PRC_IFDOWN)
575 sin = &((struct sockaddr_in *)arg)->sin_addr;
576 else if (cmd == PRC_HOSTDEAD || cmd == PRC_HOSTUNREACH)
577 sin = (struct in_addr *)arg;
578 else
579 sin = &((struct icmp *)arg)->icmp_ip.ip_dst;
39674d5f
SL
580 in_pcbnotify(&tcb, sin, inetctlerrmap[cmd], tcp_abort);
581 in_pcbnotify(&udb, sin, inetctlerrmap[cmd], udp_abort);
72e4f44e
SL
582}
583
584int ipprintfs = 0;
585int ipforwarding = 1;
586/*
587 * Forward a packet. If some error occurs return the sender
588 * and icmp packet. Note we can't always generate a meaningful
589 * icmp message because icmp doesn't have a large enough repetoire
590 * of codes and types.
591 */
592ip_forward(ip)
593 register struct ip *ip;
594{
595 register int error, type, code;
67387c9c 596 struct mbuf *mopt, *mcopy;
72e4f44e
SL
597
598 if (ipprintfs)
599 printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
600 ip->ip_dst, ip->ip_ttl);
601 if (ipforwarding == 0) {
602 /* can't tell difference between net and host */
603 type = ICMP_UNREACH, code = ICMP_UNREACH_NET;
604 goto sendicmp;
605 }
606 if (ip->ip_ttl < IPTTLDEC) {
607 type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
608 goto sendicmp;
609 }
610 ip->ip_ttl -= IPTTLDEC;
611 mopt = m_get(M_DONTWAIT);
612 if (mopt == 0) {
613 m_freem(dtom(ip));
614 return;
615 }
67387c9c
SL
616
617 /*
618 * Save at most 64 bytes of the packet in case
619 * we need to generate an ICMP message to the src.
620 */
348901af 621 mcopy = m_copy(dtom(ip), 0, imin(ip->ip_len, 64));
72e4f44e
SL
622 ip_stripoptions(ip, mopt);
623
624 /* last 0 here means no directed broadcast */
67387c9c
SL
625 if ((error = ip_output(dtom(ip), mopt, 0, 0)) == 0) {
626 if (mcopy)
627 m_freem(mcopy);
72e4f44e 628 return;
67387c9c
SL
629 }
630 ip = mtod(mcopy, struct ip *);
631 type = ICMP_UNREACH, code = 0; /* need ``undefined'' */
632 switch (error) {
633
634 case ENETUNREACH:
635 case ENETDOWN:
72e4f44e 636 code = ICMP_UNREACH_NET;
67387c9c
SL
637 break;
638
639 case EMSGSIZE:
72e4f44e 640 code = ICMP_UNREACH_NEEDFRAG;
67387c9c
SL
641 break;
642
643 case EPERM:
644 code = ICMP_UNREACH_PORT;
645 break;
646
647 case ENOBUFS:
648 type = ICMP_SOURCEQUENCH;
649 break;
650
651 case EHOSTDOWN:
652 case EHOSTUNREACH:
653 code = ICMP_UNREACH_HOST;
654 break;
655 }
72e4f44e
SL
656sendicmp:
657 icmp_error(ip, type, code);
658}