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