merge in NFS code from Rick Macklem
[unix-history] / usr / src / sys / netinet / ip_input.c
CommitLineData
8ae0e4b4 1/*
b293fc3e 2 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
2b6b6284 3 * All rights reserved.
8ae0e4b4 4 *
2b6b6284 5 * Redistribution and use in source and binary forms are permitted
616d42db
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2b6b6284 16 *
9759b0a4 17 * @(#)ip_input.c 7.12 (Berkeley) %G%
8ae0e4b4 18 */
6e8b2eca 19
20666ad3
JB
20#include "param.h"
21#include "systm.h"
b293fc3e 22#include "malloc.h"
20666ad3
JB
23#include "mbuf.h"
24#include "domain.h"
25#include "protosw.h"
26#include "socket.h"
27#include "errno.h"
28#include "time.h"
29#include "kernel.h"
6e7edb25
BJ
30
31#include "../net/if.h"
32#include "../net/route.h"
f4d55810 33
20666ad3
JB
34#include "in.h"
35#include "in_pcb.h"
36#include "in_systm.h"
f223fa7d 37#include "in_var.h"
20666ad3
JB
38#include "ip.h"
39#include "ip_var.h"
40#include "ip_icmp.h"
41#include "tcp.h"
e6dd2097 42
b293fc3e
MK
43#ifndef IPFORWARDING
44#ifdef GATEWAY
45#define IPFORWARDING 1
46#else /* GATEWAY */
47#define IPFORWARDING 0
48#endif /* GATEWAY */
49#endif /* IPFORWARDING */
50#ifndef IPSENDREDIRECTS
51#define IPSENDREDIRECTS 1
52#endif
53int ipprintfs = 0;
54int ipforwarding = IPFORWARDING;
55extern int in_interfaces;
56int ipsendredirects = IPSENDREDIRECTS;
57
21f50054
MK
58#ifndef IPFORWARDING
59#ifdef GATEWAY
60#define IPFORWARDING 1
61#else /* GATEWAY */
62#define IPFORWARDING 0
63#endif /* GATEWAY */
64#endif /* IPFORWARDING */
65#ifndef IPSENDREDIRECTS
66#define IPSENDREDIRECTS 1
67#endif
68int ipprintfs = 0;
69int ipforwarding = IPFORWARDING;
70extern int in_interfaces;
71int ipsendredirects = IPSENDREDIRECTS;
72
eb44bfb2 73u_char ip_protox[IPPROTO_MAX];
1e977657 74int ipqmaxlen = IFQ_MAXLEN;
f223fa7d 75struct in_ifaddr *in_ifaddr; /* first inet address */
eb44bfb2 76
8fb48289
MK
77/*
78 * We need to save the IP options in case a protocol wants to respond
79 * to an incoming packet over the same route if the packet got here
80 * using IP source routing. This allows connection establishment and
81 * maintenance when the remote end is on a network that is not known
82 * to us.
83 */
84int ip_nhops = 0;
85static struct ip_srcrt {
21f50054 86 struct in_addr dst; /* final destination */
b293fc3e 87 struct in_addr dst; /* final destination */
8fb48289
MK
88 char nop; /* one NOP to align */
89 char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */
b293fc3e 90 struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
8fb48289
MK
91} ip_srcrt;
92
d52566dd 93/*
b454c3ea 94 * IP initialization: fill in IP protocol switch table.
405c9168 95 * All protocols not implemented in kernel go to raw IP protocol handler.
d52566dd
BJ
96 */
97ip_init()
98{
eb44bfb2
BJ
99 register struct protosw *pr;
100 register int i;
eb44bfb2 101
8fb48289 102 pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
eb44bfb2
BJ
103 if (pr == 0)
104 panic("ip_init");
105 for (i = 0; i < IPPROTO_MAX; i++)
59965020
BJ
106 ip_protox[i] = pr - inetsw;
107 for (pr = inetdomain.dom_protosw;
36bb5f94 108 pr < inetdomain.dom_protoswNPROTOSW; pr++)
abe04898 109 if (pr->pr_domain->dom_family == PF_INET &&
eb44bfb2 110 pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
59965020 111 ip_protox[pr->pr_protocol] = pr - inetsw;
d52566dd 112 ipq.next = ipq.prev = &ipq;
b2ac7f3b 113 ip_id = time.tv_sec & 0xffff;
1e977657 114 ipintrq.ifq_maxlen = ipqmaxlen;
d52566dd
BJ
115}
116
e6dd2097 117struct ip *ip_reass();
b293fc3e 118struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
8fb48289 119struct route ipforward_rt;
e6dd2097 120
e6dd2097
BJ
121/*
122 * Ip input routine. Checksum and byte swap header. If fragmented
123 * try to reassamble. If complete and fragment queue exists, discard.
124 * Process options. Pass to next level.
125 */
8a13b737 126ipintr()
e1d82856 127{
2b4b57cd 128 register struct ip *ip;
8a13b737 129 register struct mbuf *m;
e1d82856 130 register struct ipq *fp;
f223fa7d 131 register struct in_ifaddr *ia;
8a13b737 132 int hlen, s;
e1d82856 133
8a13b737 134next:
e6dd2097 135 /*
8a13b737
BJ
136 * Get next datagram off input queue and get IP header
137 * in first mbuf.
e6dd2097 138 */
8a13b737 139 s = splimp();
b293fc3e 140 IF_DEQUEUE(&ipintrq, m);
8a13b737 141 splx(s);
7ac98d3c 142 if (m == 0)
8a13b737 143 return;
b293fc3e
MK
144if ((m->m_flags & M_PKTHDR) == 0)
145panic("ipintr no HDR");
773dfe90
MK
146 /*
147 * If no IP addresses have been set yet but the interfaces
148 * are receiving, can't do anything with incoming packets yet.
149 */
150 if (in_ifaddr == NULL)
151 goto bad;
7922844b 152 ipstat.ips_total++;
b293fc3e 153 if ((m->m_flags & M_EXT || m->m_len < sizeof (struct ip)) &&
9dc8d46a
SL
154 (m = m_pullup(m, sizeof (struct ip))) == 0) {
155 ipstat.ips_toosmall++;
156 goto next;
157 }
e6dd2097 158 ip = mtod(m, struct ip *);
f223fa7d 159 hlen = ip->ip_hl << 2;
8fb48289 160 if (hlen < sizeof(struct ip)) { /* minimum header length */
f223fa7d 161 ipstat.ips_badhlen++;
ac066ae1 162 goto bad;
f223fa7d
MK
163 }
164 if (hlen > m->m_len) {
9dc8d46a
SL
165 if ((m = m_pullup(m, hlen)) == 0) {
166 ipstat.ips_badhlen++;
167 goto next;
168 }
405c9168
BJ
169 ip = mtod(m, struct ip *);
170 }
b293fc3e
MK
171 if (ip->ip_sum = in_cksum(m, hlen)) {
172 ipstat.ips_badsum++;
173 goto bad;
174 }
4ad99bae
BJ
175
176 /*
177 * Convert fields to host representation.
178 */
cdad2eb1 179 ip->ip_len = ntohs((u_short)ip->ip_len);
9dc8d46a
SL
180 if (ip->ip_len < hlen) {
181 ipstat.ips_badlen++;
182 goto bad;
183 }
e6dd2097 184 ip->ip_id = ntohs(ip->ip_id);
4ad99bae 185 ip->ip_off = ntohs((u_short)ip->ip_off);
e1d82856 186
d10bd5b7 187 /*
e6dd2097
BJ
188 * Check that the amount of data in the buffers
189 * is as at least much as the IP header would have us expect.
190 * Trim mbufs if longer than we expect.
191 * Drop packet if shorter than we expect.
d10bd5b7 192 */
b293fc3e
MK
193 if (m->m_pkthdr.len < ip->ip_len) {
194 ipstat.ips_tooshort++;
195 goto bad;
1dd55890 196 }
b293fc3e
MK
197 if (m->m_pkthdr.len > ip->ip_len) {
198 if (m->m_len == m->m_pkthdr.len) {
199 m->m_len = ip->ip_len;
200 m->m_pkthdr.len = ip->ip_len;
201 } else
202 m_adj(m, ip->ip_len - m->m_pkthdr.len);
d10bd5b7 203 }
e1d82856 204
e6dd2097
BJ
205 /*
206 * Process options and, if not destined for us,
72e4f44e
SL
207 * ship it on. ip_dooptions returns 1 when an
208 * error was detected (causing an icmp message
ac066ae1 209 * to be sent and the original packet to be freed).
e6dd2097 210 */
8fb48289 211 ip_nhops = 0; /* for source routed packets */
b293fc3e 212 if (hlen > sizeof (struct ip) && ip_dooptions(m))
72e4f44e 213 goto next;
ee787340
SL
214
215 /*
f223fa7d 216 * Check our list of addresses, to see if the packet is for us.
ee787340 217 */
f223fa7d
MK
218 for (ia = in_ifaddr; ia; ia = ia->ia_next) {
219#define satosin(sa) ((struct sockaddr_in *)(sa))
220
221 if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
8fb48289 222 goto ours;
4afb57fa
MK
223 if (
224#ifdef DIRECTED_BROADCAST
b293fc3e 225 ia->ia_ifp == m->m_pkthdr.rcvif &&
4afb57fa
MK
226#endif
227 (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
b2a3d559 228 u_long t;
4afb57fa
MK
229
230 if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
231 ip->ip_dst.s_addr)
232 goto ours;
233 if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
234 goto ours;
235 /*
236 * Look for all-0's host part (old broadcast addr),
237 * either for subnet or net.
238 */
b2a3d559
MK
239 t = ntohl(ip->ip_dst.s_addr);
240 if (t == ia->ia_subnet)
4afb57fa 241 goto ours;
b2a3d559 242 if (t == ia->ia_net)
4afb57fa
MK
243 goto ours;
244 }
d10bd5b7 245 }
8fb48289
MK
246 if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
247 goto ours;
248 if (ip->ip_dst.s_addr == INADDR_ANY)
249 goto ours;
e1d82856 250
8fb48289
MK
251 /*
252 * Not for us; forward if possible and desirable.
253 */
b293fc3e
MK
254 if (ipforwarding == 0
255#ifndef GATEWAY
256 || in_interfaces <= 1
257#endif
258 ) {
259 ipstat.ips_cantforward++;
260 m_freem(m);
261 } else
262 ip_forward(m);
8fb48289
MK
263 goto next;
264
265ours:
e6dd2097 266 /*
5828b179
MK
267 * If offset or IP_MF are set, must reassemble.
268 * Otherwise, nothing need be done.
269 * (We could look in the reassembly queue to see
270 * if the packet was previously fragmented,
271 * but it's not worth the time; just let them time out.)
e6dd2097 272 */
5828b179
MK
273 if (ip->ip_off &~ IP_DF) {
274 /*
275 * Look for queue of fragments
276 * of this datagram.
277 */
278 for (fp = ipq.next; fp != &ipq; fp = fp->next)
279 if (ip->ip_id == fp->ipq_id &&
280 ip->ip_src.s_addr == fp->ipq_src.s_addr &&
281 ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
282 ip->ip_p == fp->ipq_p)
283 goto found;
284 fp = 0;
e6dd2097 285found:
e1d82856 286
5828b179
MK
287 /*
288 * Adjust ip_len to not reflect header,
289 * set ip_mff if more fragments are expected,
290 * convert offset of this to bytes.
291 */
292 ip->ip_len -= hlen;
293 ((struct ipasfrag *)ip)->ipf_mff = 0;
294 if (ip->ip_off & IP_MF)
295 ((struct ipasfrag *)ip)->ipf_mff = 1;
296 ip->ip_off <<= 3;
e1d82856 297
5828b179
MK
298 /*
299 * If datagram marked as having more fragments
300 * or if this is not the first fragment,
301 * attempt reassembly; if it succeeds, proceed.
302 */
303 if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) {
304 ipstat.ips_fragments++;
305 ip = ip_reass((struct ipasfrag *)ip, fp);
306 if (ip == 0)
307 goto next;
308 m = dtom(ip);
309 } else
310 if (fp)
311 ip_freef(fp);
e6dd2097 312 } else
5828b179 313 ip->ip_len -= hlen;
4ad99bae
BJ
314
315 /*
316 * Switch out to protocol's input routine.
317 */
b293fc3e 318 (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
8a13b737 319 goto next;
4ad99bae
BJ
320bad:
321 m_freem(m);
8a13b737 322 goto next;
e6dd2097 323}
e1d82856 324
e6dd2097
BJ
325/*
326 * Take incoming datagram fragment and try to
4ad99bae 327 * reassemble it into whole datagram. If a chain for
e6dd2097
BJ
328 * reassembly of this datagram already exists, then it
329 * is given as fp; otherwise have to make a chain.
330 */
331struct ip *
332ip_reass(ip, fp)
eb44bfb2 333 register struct ipasfrag *ip;
e6dd2097
BJ
334 register struct ipq *fp;
335{
336 register struct mbuf *m = dtom(ip);
eb44bfb2 337 register struct ipasfrag *q;
e6dd2097
BJ
338 struct mbuf *t;
339 int hlen = ip->ip_hl << 2;
340 int i, next;
d10bd5b7 341
e6dd2097
BJ
342 /*
343 * Presence of header sizes in mbufs
344 * would confuse code below.
345 */
b293fc3e 346 m->m_data += hlen;
e6dd2097 347 m->m_len -= hlen;
d10bd5b7 348
e6dd2097
BJ
349 /*
350 * If first fragment to arrive, create a reassembly queue.
351 */
352 if (fp == 0) {
13494852 353 if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
e6dd2097 354 goto dropfrag;
e6dd2097
BJ
355 fp = mtod(t, struct ipq *);
356 insque(fp, &ipq);
357 fp->ipq_ttl = IPFRAGTTL;
358 fp->ipq_p = ip->ip_p;
359 fp->ipq_id = ip->ip_id;
eb44bfb2
BJ
360 fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
361 fp->ipq_src = ((struct ip *)ip)->ip_src;
362 fp->ipq_dst = ((struct ip *)ip)->ip_dst;
405c9168
BJ
363 q = (struct ipasfrag *)fp;
364 goto insert;
e6dd2097 365 }
e1d82856 366
e6dd2097
BJ
367 /*
368 * Find a segment which begins after this one does.
369 */
eb44bfb2 370 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
e6dd2097
BJ
371 if (q->ip_off > ip->ip_off)
372 break;
e1d82856 373
e6dd2097
BJ
374 /*
375 * If there is a preceding segment, it may provide some of
376 * our data already. If so, drop the data from the incoming
377 * segment. If it provides all of our data, drop us.
378 */
eb44bfb2
BJ
379 if (q->ipf_prev != (struct ipasfrag *)fp) {
380 i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
e6dd2097
BJ
381 if (i > 0) {
382 if (i >= ip->ip_len)
383 goto dropfrag;
384 m_adj(dtom(ip), i);
385 ip->ip_off += i;
386 ip->ip_len -= i;
e1d82856 387 }
d10bd5b7 388 }
e1d82856 389
e6dd2097
BJ
390 /*
391 * While we overlap succeeding segments trim them or,
392 * if they are completely covered, dequeue them.
393 */
eb44bfb2 394 while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
e6dd2097
BJ
395 i = (ip->ip_off + ip->ip_len) - q->ip_off;
396 if (i < q->ip_len) {
397 q->ip_len -= i;
c107df34 398 q->ip_off += i;
e6dd2097
BJ
399 m_adj(dtom(q), i);
400 break;
401 }
eb44bfb2
BJ
402 q = q->ipf_next;
403 m_freem(dtom(q->ipf_prev));
404 ip_deq(q->ipf_prev);
e6dd2097 405 }
e1d82856 406
405c9168 407insert:
e6dd2097
BJ
408 /*
409 * Stick new segment in its place;
410 * check for complete reassembly.
411 */
eb44bfb2 412 ip_enq(ip, q->ipf_prev);
e6dd2097 413 next = 0;
eb44bfb2 414 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
e6dd2097
BJ
415 if (q->ip_off != next)
416 return (0);
417 next += q->ip_len;
418 }
eb44bfb2 419 if (q->ipf_prev->ipf_mff)
e6dd2097 420 return (0);
e1d82856 421
e6dd2097
BJ
422 /*
423 * Reassembly is complete; concatenate fragments.
424 */
425 q = fp->ipq_next;
426 m = dtom(q);
427 t = m->m_next;
428 m->m_next = 0;
429 m_cat(m, t);
dfb346d0
BJ
430 q = q->ipf_next;
431 while (q != (struct ipasfrag *)fp) {
432 t = dtom(q);
433 q = q->ipf_next;
434 m_cat(m, t);
435 }
e1d82856 436
e6dd2097
BJ
437 /*
438 * Create header for new ip packet by
439 * modifying header of first packet;
440 * dequeue and discard fragment reassembly header.
441 * Make header visible.
442 */
443 ip = fp->ipq_next;
444 ip->ip_len = next;
eb44bfb2
BJ
445 ((struct ip *)ip)->ip_src = fp->ipq_src;
446 ((struct ip *)ip)->ip_dst = fp->ipq_dst;
e6dd2097 447 remque(fp);
cdad2eb1 448 (void) m_free(dtom(fp));
e6dd2097 449 m = dtom(ip);
8fb48289 450 m->m_len += (ip->ip_hl << 2);
b293fc3e 451 m->m_data -= (ip->ip_hl << 2);
eb44bfb2 452 return ((struct ip *)ip);
e6dd2097
BJ
453
454dropfrag:
8fb48289 455 ipstat.ips_fragdropped++;
e6dd2097
BJ
456 m_freem(m);
457 return (0);
e1d82856
BJ
458}
459
e6dd2097
BJ
460/*
461 * Free a fragment reassembly header and all
462 * associated datagrams.
463 */
e6dd2097
BJ
464ip_freef(fp)
465 struct ipq *fp;
e1d82856 466{
e16de434 467 register struct ipasfrag *q, *p;
e6dd2097 468
e16de434
SL
469 for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
470 p = q->ipf_next;
471 ip_deq(q);
e6dd2097 472 m_freem(dtom(q));
e16de434
SL
473 }
474 remque(fp);
475 (void) m_free(dtom(fp));
e1d82856
BJ
476}
477
e6dd2097
BJ
478/*
479 * Put an ip fragment on a reassembly chain.
480 * Like insque, but pointers in middle of structure.
481 */
482ip_enq(p, prev)
eb44bfb2 483 register struct ipasfrag *p, *prev;
e1d82856 484{
e1d82856 485
eb44bfb2
BJ
486 p->ipf_prev = prev;
487 p->ipf_next = prev->ipf_next;
488 prev->ipf_next->ipf_prev = p;
489 prev->ipf_next = p;
e1d82856
BJ
490}
491
e6dd2097
BJ
492/*
493 * To ip_enq as remque is to insque.
494 */
495ip_deq(p)
eb44bfb2 496 register struct ipasfrag *p;
e1d82856 497{
e6dd2097 498
eb44bfb2
BJ
499 p->ipf_prev->ipf_next = p->ipf_next;
500 p->ipf_next->ipf_prev = p->ipf_prev;
e1d82856
BJ
501}
502
e6dd2097
BJ
503/*
504 * IP timer processing;
505 * if a timer expires on a reassembly
506 * queue, discard it.
507 */
d52566dd 508ip_slowtimo()
e1d82856
BJ
509{
510 register struct ipq *fp;
e6dd2097 511 int s = splnet();
e1d82856 512
4aed14e3
BJ
513 fp = ipq.next;
514 if (fp == 0) {
515 splx(s);
516 return;
517 }
e16de434
SL
518 while (fp != &ipq) {
519 --fp->ipq_ttl;
520 fp = fp->next;
8fb48289
MK
521 if (fp->prev->ipq_ttl == 0) {
522 ipstat.ips_fragtimeout++;
e16de434 523 ip_freef(fp->prev);
8fb48289 524 }
e16de434 525 }
e6dd2097 526 splx(s);
e1d82856
BJ
527}
528
4ad99bae
BJ
529/*
530 * Drain off all datagram fragments.
531 */
d52566dd
BJ
532ip_drain()
533{
534
8fb48289
MK
535 while (ipq.next != &ipq) {
536 ipstat.ips_fragdropped++;
e16de434 537 ip_freef(ipq.next);
8fb48289 538 }
d52566dd 539}
2b4b57cd 540
1846019d 541extern struct in_ifaddr *ifptoia();
8fb48289
MK
542struct in_ifaddr *ip_rtaddr();
543
e6dd2097
BJ
544/*
545 * Do option processing on a datagram,
546 * possibly discarding it if bad options
547 * are encountered.
548 */
b293fc3e
MK
549ip_dooptions(m)
550 struct mbuf *m;
e1d82856 551{
21f50054 552 register struct ip *ip = mtod(m, struct ip *);
b293fc3e 553 register struct ip *ip = mtod(m, struct ip *);
e6dd2097 554 register u_char *cp;
d52566dd 555 register struct ip_timestamp *ipt;
8fb48289 556 register struct in_ifaddr *ia;
b293fc3e 557 int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
21f50054 558 int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
8fb48289
MK
559 struct in_addr *sin;
560 n_time ntime;
e6dd2097
BJ
561
562 cp = (u_char *)(ip + 1);
563 cnt = (ip->ip_hl << 2) - sizeof (struct ip);
564 for (; cnt > 0; cnt -= optlen, cp += optlen) {
8fb48289 565 opt = cp[IPOPT_OPTVAL];
e6dd2097
BJ
566 if (opt == IPOPT_EOL)
567 break;
568 if (opt == IPOPT_NOP)
569 optlen = 1;
942169f7 570 else {
8fb48289
MK
571 optlen = cp[IPOPT_OLEN];
572 if (optlen <= 0 || optlen > cnt) {
573 code = &cp[IPOPT_OLEN] - (u_char *)ip;
36bb5f94 574 goto bad;
8fb48289 575 }
942169f7 576 }
e6dd2097 577 switch (opt) {
e1d82856 578
e6dd2097
BJ
579 default:
580 break;
e1d82856 581
4ad99bae
BJ
582 /*
583 * Source routing with record.
584 * Find interface with current destination address.
585 * If none on this machine then drop if strictly routed,
586 * or do nothing if loosely routed.
587 * Record interface address and bring up next address
588 * component. If strictly routed make sure next
589 * address on directly accessible net.
590 */
e6dd2097 591 case IPOPT_LSRR:
a71ece0a 592 case IPOPT_SSRR:
8fb48289
MK
593 if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
594 code = &cp[IPOPT_OFFSET] - (u_char *)ip;
595 goto bad;
596 }
597 ipaddr.sin_addr = ip->ip_dst;
598 ia = (struct in_ifaddr *)
599 ifa_ifwithaddr((struct sockaddr *)&ipaddr);
600 if (ia == 0) {
601 if (opt == IPOPT_SSRR) {
602 type = ICMP_UNREACH;
603 code = ICMP_UNREACH_SRCFAIL;
4ad99bae 604 goto bad;
8fb48289
MK
605 }
606 /*
607 * Loose routing, and not at next destination
608 * yet; nothing to do except forward.
609 */
4ad99bae 610 break;
e6dd2097 611 }
8fb48289
MK
612 off--; /* 0 origin */
613 if (off > optlen - sizeof(struct in_addr)) {
614 /*
615 * End of source route. Should be for us.
616 */
617 save_rte(cp, ip->ip_src);
4ad99bae 618 break;
8fb48289
MK
619 }
620 /*
621 * locate outgoing interface
622 */
8011f5df 623 bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
8fb48289
MK
624 sizeof(ipaddr.sin_addr));
625 if ((opt == IPOPT_SSRR &&
626 in_iaonnetof(in_netof(ipaddr.sin_addr)) == 0) ||
627 (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
628 type = ICMP_UNREACH;
629 code = ICMP_UNREACH_SRCFAIL;
4ad99bae 630 goto bad;
8fb48289
MK
631 }
632 ip->ip_dst = ipaddr.sin_addr;
8011f5df
MK
633 bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
634 (caddr_t)(cp + off), sizeof(struct in_addr));
8fb48289 635 cp[IPOPT_OFFSET] += sizeof(struct in_addr);
b293fc3e 636 forward = 1;
21f50054 637 forward = 1;
8fb48289
MK
638 break;
639
640 case IPOPT_RR:
641 if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
642 code = &cp[IPOPT_OFFSET] - (u_char *)ip;
643 goto bad;
644 }
645 /*
646 * If no space remains, ignore.
647 */
648 off--; /* 0 origin */
649 if (off > optlen - sizeof(struct in_addr))
650 break;
c13e44b4 651 bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
8fb48289
MK
652 sizeof(ipaddr.sin_addr));
653 /*
9759b0a4
MK
654 * locate outgoing interface; if we're the destination,
655 * use the incoming interface (should be same).
8fb48289 656 */
9759b0a4
MK
657 if ((ia =
658 (struct in_ifaddr *)ifa_ifwithaddr(
659 (struct sockaddr *)&ipaddr)) == 0 &&
660 (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
8fb48289 661 type = ICMP_UNREACH;
d89cc5db 662 code = ICMP_UNREACH_HOST;
8fb48289
MK
663 goto bad;
664 }
8011f5df
MK
665 bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
666 (caddr_t)(cp + off), sizeof(struct in_addr));
8fb48289 667 cp[IPOPT_OFFSET] += sizeof(struct in_addr);
e6dd2097
BJ
668 break;
669
670 case IPOPT_TS:
72e4f44e 671 code = cp - (u_char *)ip;
d52566dd
BJ
672 ipt = (struct ip_timestamp *)cp;
673 if (ipt->ipt_len < 5)
e6dd2097 674 goto bad;
d52566dd
BJ
675 if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
676 if (++ipt->ipt_oflw == 0)
e6dd2097 677 goto bad;
e6dd2097
BJ
678 break;
679 }
1846019d 680 sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
d52566dd 681 switch (ipt->ipt_flg) {
e1d82856 682
e6dd2097
BJ
683 case IPOPT_TS_TSONLY:
684 break;
e1d82856 685
e6dd2097 686 case IPOPT_TS_TSANDADDR:
8fb48289
MK
687 if (ipt->ipt_ptr + sizeof(n_time) +
688 sizeof(struct in_addr) > ipt->ipt_len)
e6dd2097 689 goto bad;
b293fc3e 690 ia = ifptoia(m->m_pkthdr.rcvif);
1846019d 691 bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
8fb48289 692 (caddr_t)sin, sizeof(struct in_addr));
1846019d 693 ipt->ipt_ptr += sizeof(struct in_addr);
e6dd2097
BJ
694 break;
695
696 case IPOPT_TS_PRESPEC:
1846019d
MK
697 if (ipt->ipt_ptr + sizeof(n_time) +
698 sizeof(struct in_addr) > ipt->ipt_len)
699 goto bad;
8fb48289
MK
700 bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
701 sizeof(struct in_addr));
f223fa7d 702 if (ifa_ifwithaddr((struct sockaddr *)&ipaddr) == 0)
4ad99bae 703 continue;
8fb48289 704 ipt->ipt_ptr += sizeof(struct in_addr);
e1d82856
BJ
705 break;
706
707 default:
e6dd2097 708 goto bad;
e1d82856 709 }
8fb48289 710 ntime = iptime();
1846019d
MK
711 bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
712 sizeof(n_time));
8fb48289 713 ipt->ipt_ptr += sizeof(n_time);
e6dd2097 714 }
e1d82856 715 }
b293fc3e
MK
716 if (forward) {
717 ip_forward(m);
718 return (1);
719 } else
720 return (0);
e6dd2097 721bad:
b293fc3e 722 icmp_error(m, type, code);
72e4f44e 723 return (1);
e1d82856
BJ
724}
725
8fb48289
MK
726/*
727 * Given address of next destination (final or next hop),
728 * return internet address info of interface to be used to get there.
729 */
730struct in_ifaddr *
731ip_rtaddr(dst)
732 struct in_addr dst;
733{
734 register struct sockaddr_in *sin;
735 register struct in_ifaddr *ia;
736
737 sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
738
739 if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
740 if (ipforward_rt.ro_rt) {
741 RTFREE(ipforward_rt.ro_rt);
742 ipforward_rt.ro_rt = 0;
743 }
744 sin->sin_family = AF_INET;
b293fc3e 745 sin->sin_len = sizeof(*sin);
8fb48289
MK
746 sin->sin_addr = dst;
747
748 rtalloc(&ipforward_rt);
749 }
750 if (ipforward_rt.ro_rt == 0)
751 return ((struct in_ifaddr *)0);
752 /*
753 * Find address associated with outgoing interface.
754 */
755 for (ia = in_ifaddr; ia; ia = ia->ia_next)
756 if (ia->ia_ifp == ipforward_rt.ro_rt->rt_ifp)
757 break;
758 return (ia);
759}
760
761/*
762 * Save incoming source route for use in replies,
763 * to be picked up later by ip_srcroute if the receiver is interested.
764 */
765save_rte(option, dst)
8011f5df 766 u_char *option;
8fb48289
MK
767 struct in_addr dst;
768{
8011f5df 769 unsigned olen;
8fb48289
MK
770
771 olen = option[IPOPT_OLEN];
b293fc3e
MK
772 if (ipprintfs)
773 printf("save_rte: olen %d\n", olen);
774 if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
8fb48289 775 return;
8011f5df 776 bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
8fb48289 777 ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
b293fc3e 778 ip_srcrt.dst = dst;
8fb48289
MK
779}
780
781/*
782 * Retrieve incoming source route for use in replies,
783 * in the same form used by setsockopt.
784 * The first hop is placed before the options, will be removed later.
785 */
786struct mbuf *
787ip_srcroute()
788{
789 register struct in_addr *p, *q;
790 register struct mbuf *m;
791
792 if (ip_nhops == 0)
793 return ((struct mbuf *)0);
13494852
MK
794 m = m_get(M_DONTWAIT, MT_SOOPTS);
795 if (m == 0)
796 return ((struct mbuf *)0);
b293fc3e 797
21f50054
MK
798#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
799
800 /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
801 m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
802 OPTSIZ;
803 if (ipprintfs)
804 printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
805
b293fc3e
MK
806#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
807
808 /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
809 m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
810 OPTSIZ;
811 if (ipprintfs)
812 printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
8fb48289
MK
813
814 /*
815 * First save first hop for return route
816 */
817 p = &ip_srcrt.route[ip_nhops - 1];
818 *(mtod(m, struct in_addr *)) = *p--;
b293fc3e
MK
819 if (ipprintfs)
820 printf(" hops %X", ntohl(*mtod(m, struct in_addr *)));
21f50054
MK
821 if (ipprintfs)
822 printf(" hops %X", ntohl(*mtod(m, struct in_addr *)));
8fb48289
MK
823
824 /*
825 * Copy option fields and padding (nop) to mbuf.
826 */
827 ip_srcrt.nop = IPOPT_NOP;
b293fc3e
MK
828 ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
829 bcopy((caddr_t)&ip_srcrt.nop,
830 mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
8fb48289 831 q = (struct in_addr *)(mtod(m, caddr_t) +
b293fc3e
MK
832 sizeof(struct in_addr) + OPTSIZ);
833#undef OPTSIZ
8fb48289
MK
834 /*
835 * Record return path as an IP source route,
836 * reversing the path (pointers are now aligned).
837 */
b293fc3e
MK
838 while (p >= ip_srcrt.route) {
839 if (ipprintfs)
840 printf(" %X", ntohl(*q));
8fb48289 841 *q++ = *p--;
b293fc3e
MK
842 }
843 /*
844 * Last hop goes to final destination.
845 */
846 *q = ip_srcrt.dst;
21f50054
MK
847 if (ipprintfs)
848 printf(" %X\n", ntohl(*q));
849 }
850 /*
851 * Last hop goes to final destination.
852 */
853 *q = ip_srcrt.dst;
b293fc3e
MK
854 if (ipprintfs)
855 printf(" %X\n", ntohl(*q));
8fb48289
MK
856 return (m);
857}
858
e6dd2097 859/*
4ad99bae
BJ
860 * Strip out IP options, at higher
861 * level protocol in the kernel.
862 * Second argument is buffer to which options
863 * will be moved, and return value is their length.
21f50054
MK
864#ifdef NEW
865 * XXX should be deleted; last arg currently ignored.
866#endif NEW
b293fc3e 867 * XXX should be deleted; last arg currently ignored.
e6dd2097 868 */
b293fc3e
MK
869ip_stripoptions(m, mopt)
870 register struct mbuf *m;
7c08c626 871 struct mbuf *mopt;
e1d82856 872{
e6dd2097 873 register int i;
b293fc3e 874 struct ip *ip = mtod(m, struct ip *);
8fb48289 875 register caddr_t opts;
e6dd2097 876 int olen;
e6dd2097
BJ
877
878 olen = (ip->ip_hl<<2) - sizeof (struct ip);
8fb48289 879 opts = (caddr_t)(ip + 1);
e6dd2097 880 i = m->m_len - (sizeof (struct ip) + olen);
8fb48289 881 bcopy(opts + olen, opts, (unsigned)i);
4aed14e3 882 m->m_len -= olen;
b293fc3e
MK
883 if (m->m_flags & M_PKTHDR)
884 m->m_pkthdr.len -= olen;
8fb48289 885 ip->ip_hl = sizeof(struct ip) >> 2;
e1d82856 886}
72e4f44e 887
0ecfeefb 888u_char inetctlerrmap[PRC_NCMDS] = {
8fb48289 889 0, 0, 0, 0,
0ecfeefb
SL
890 0, 0, EHOSTDOWN, EHOSTUNREACH,
891 ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
8fb48289
MK
892 EMSGSIZE, EHOSTUNREACH, 0, 0,
893 0, 0, 0, 0,
894 ENOPROTOOPT
72e4f44e
SL
895};
896
72e4f44e
SL
897/*
898 * Forward a packet. If some error occurs return the sender
f223fa7d 899 * an icmp packet. Note we can't always generate a meaningful
8fb48289 900 * icmp message because icmp doesn't have a large enough repertoire
72e4f44e 901 * of codes and types.
c0a9b2bd
MK
902 *
903 * If not forwarding (possibly because we have only a single external
904 * network), just drop the packet. This could be confusing if ipforwarding
905 * was zero but some routing protocol was advancing us as a gateway
906 * to somewhere. However, we must let the routing protocol deal with that.
72e4f44e 907 */
b293fc3e
MK
908ip_forward(m)
909 struct mbuf *m;
72e4f44e 910{
21f50054 911 register struct ip *ip = mtod(m, struct ip *);
b293fc3e 912 register struct ip *ip = mtod(m, struct ip *);
8fb48289
MK
913 register int error, type = 0, code;
914 register struct sockaddr_in *sin;
f223fa7d 915 struct mbuf *mcopy;
8fb48289 916 struct in_addr dest;
72e4f44e 917
8fb48289 918 dest.s_addr = 0;
72e4f44e
SL
919 if (ipprintfs)
920 printf("forward: src %x dst %x ttl %x\n", ip->ip_src,
921 ip->ip_dst, ip->ip_ttl);
b293fc3e 922 if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
c0a9b2bd 923 ipstat.ips_cantforward++;
b293fc3e 924 m_freem(m);
c0a9b2bd 925 return;
72e4f44e 926 }
21f50054 927 ip->ip_id = htons(ip->ip_id);
c13e44b4 928 if (ip->ip_ttl <= IPTTLDEC) {
72e4f44e
SL
929 type = ICMP_TIMXCEED, code = ICMP_TIMXCEED_INTRANS;
930 goto sendicmp;
931 }
932 ip->ip_ttl -= IPTTLDEC;
67387c9c
SL
933
934 /*
935 * Save at most 64 bytes of the packet in case
936 * we need to generate an ICMP message to the src.
937 */
b293fc3e 938 mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
72e4f44e 939
8fb48289
MK
940 sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
941 if (ipforward_rt.ro_rt == 0 ||
942 ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
943 if (ipforward_rt.ro_rt) {
944 RTFREE(ipforward_rt.ro_rt);
945 ipforward_rt.ro_rt = 0;
946 }
947 sin->sin_family = AF_INET;
b293fc3e 948 sin->sin_len = sizeof(*sin);
8fb48289
MK
949 sin->sin_addr = ip->ip_dst;
950
951 rtalloc(&ipforward_rt);
952 }
953 /*
954 * If forwarding packet using same interface that it came in on,
955 * perhaps should send a redirect to sender to shortcut a hop.
956 * Only send redirect if source is sending directly to us,
957 * and if packet was not source routed (or has any options).
83dfdaa9
MK
958 * Also, don't send redirect if forwarding using a default route
959 * or a route modfied by a redirect.
8fb48289 960 */
83dfdaa9 961#define satosin(sa) ((struct sockaddr_in *)(sa))
b293fc3e
MK
962 if (ipforward_rt.ro_rt &&
963 ipforward_rt.ro_rt->rt_ifp == m->m_pkthdr.rcvif &&
21f50054 964 (ipforward_rt.ro_rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
b293fc3e 965 satosin(rt_key(ipforward_rt.ro_rt))->sin_addr.s_addr != 0 &&
8fb48289
MK
966 ipsendredirects && ip->ip_hl == (sizeof(struct ip) >> 2)) {
967 struct in_ifaddr *ia;
8fb48289
MK
968 u_long src = ntohl(ip->ip_src.s_addr);
969 u_long dst = ntohl(ip->ip_dst.s_addr);
970
b293fc3e 971 if ((ia = ifptoia(m->m_pkthdr.rcvif)) &&
8fb48289
MK
972 (src & ia->ia_subnetmask) == ia->ia_subnet) {
973 if (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY)
b293fc3e 974 dest = satosin(ipforward_rt.ro_rt->rt_gateway)->sin_addr;
8fb48289
MK
975 else
976 dest = ip->ip_dst;
977 /*
978 * If the destination is reached by a route to host,
020b84ae
MK
979 * is on a subnet of a local net, or is directly
980 * on the attached net (!), use host redirect.
8fb48289
MK
981 * (We may be the correct first hop for other subnets.)
982 */
983 type = ICMP_REDIRECT;
984 code = ICMP_REDIRECT_NET;
985 if ((ipforward_rt.ro_rt->rt_flags & RTF_HOST) ||
986 (ipforward_rt.ro_rt->rt_flags & RTF_GATEWAY) == 0)
987 code = ICMP_REDIRECT_HOST;
988 else for (ia = in_ifaddr; ia = ia->ia_next; )
989 if ((dst & ia->ia_netmask) == ia->ia_net) {
020b84ae
MK
990 if (ia->ia_subnetmask != ia->ia_netmask)
991 code = ICMP_REDIRECT_HOST;
8fb48289
MK
992 break;
993 }
994 if (ipprintfs)
995 printf("redirect (%d) to %x\n", code, dest);
996 }
997 }
998
b293fc3e 999 error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING);
8fb48289
MK
1000 if (error)
1001 ipstat.ips_cantforward++;
1002 else if (type)
1003 ipstat.ips_redirectsent++;
1004 else {
67387c9c
SL
1005 if (mcopy)
1006 m_freem(mcopy);
ac066ae1 1007 ipstat.ips_forward++;
72e4f44e 1008 return;
67387c9c 1009 }
9dfd168a
SL
1010 if (mcopy == NULL)
1011 return;
67387c9c 1012 ip = mtod(mcopy, struct ip *);
8fb48289 1013 type = ICMP_UNREACH;
67387c9c
SL
1014 switch (error) {
1015
8fb48289
MK
1016 case 0: /* forwarded, but need redirect */
1017 type = ICMP_REDIRECT;
1018 /* code set above */
1019 break;
1020
67387c9c
SL
1021 case ENETUNREACH:
1022 case ENETDOWN:
d933432a
MK
1023 if (in_localaddr(ip->ip_dst))
1024 code = ICMP_UNREACH_HOST;
1025 else
1026 code = ICMP_UNREACH_NET;
67387c9c
SL
1027 break;
1028
1029 case EMSGSIZE:
72e4f44e 1030 code = ICMP_UNREACH_NEEDFRAG;
67387c9c
SL
1031 break;
1032
1033 case EPERM:
1034 code = ICMP_UNREACH_PORT;
1035 break;
1036
1037 case ENOBUFS:
1038 type = ICMP_SOURCEQUENCH;
b293fc3e 1039 code = 0;
67387c9c
SL
1040 break;
1041
1042 case EHOSTDOWN:
1043 case EHOSTUNREACH:
1044 code = ICMP_UNREACH_HOST;
1045 break;
1046 }
72e4f44e 1047sendicmp:
b293fc3e 1048 icmp_error(m, type, code, dest);
72e4f44e 1049}