|
|
1.1 root 1: /*
2: * Copyright (c) 1982, 1986, 1988, 1993
3: * The Regents of the University of California. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: *
33: * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
34: * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
35: */
36:
37: /*
38: * Changes and additions relating to SLiRP are
39: * Copyright (c) 1995 Danny Gasparovski.
40: *
41: * Please read the file COPYRIGHT for the
42: * terms and conditions of the copyright.
43: */
44:
45: #include <slirp.h>
46: #include "ip_icmp.h"
47:
48: int ip_defttl;
49: struct ipstat ipstat;
50: struct ipq ipq;
51:
52: /*
53: * IP initialization: fill in IP protocol switch table.
54: * All protocols not implemented in kernel go to raw IP protocol handler.
55: */
56: void
57: ip_init()
58: {
59: ipq.next = ipq.prev = (ipqp_32)&ipq;
60: ip_id = tt.tv_sec & 0xffff;
61: udp_init();
62: tcp_init();
63: ip_defttl = IPDEFTTL;
64: }
65:
66: /*
67: * Ip input routine. Checksum and byte swap header. If fragmented
68: * try to reassemble. Process options. Pass to next level.
69: */
70: void
71: ip_input(m)
72: struct mbuf *m;
73: {
74: register struct ip *ip;
75: int hlen;
76:
77: DEBUG_CALL("ip_input");
78: DEBUG_ARG("m = %lx", (long)m);
79: DEBUG_ARG("m_len = %d", m->m_len);
80:
81: ipstat.ips_total++;
82:
83: if (m->m_len < sizeof (struct ip)) {
84: ipstat.ips_toosmall++;
85: return;
86: }
87:
88: ip = mtod(m, struct ip *);
89:
90: if (ip->ip_v != IPVERSION) {
91: ipstat.ips_badvers++;
92: goto bad;
93: }
94:
95: hlen = ip->ip_hl << 2;
96: if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
97: ipstat.ips_badhlen++; /* or packet too short */
98: goto bad;
99: }
100:
101: /* keep ip header intact for ICMP reply
102: * ip->ip_sum = cksum(m, hlen);
103: * if (ip->ip_sum) {
104: */
105: if(cksum(m,hlen)) {
106: ipstat.ips_badsum++;
107: goto bad;
108: }
109:
110: /*
111: * Convert fields to host representation.
112: */
113: NTOHS(ip->ip_len);
114: if (ip->ip_len < hlen) {
115: ipstat.ips_badlen++;
116: goto bad;
117: }
118: NTOHS(ip->ip_id);
119: NTOHS(ip->ip_off);
120:
121: /*
122: * Check that the amount of data in the buffers
123: * is as at least much as the IP header would have us expect.
124: * Trim mbufs if longer than we expect.
125: * Drop packet if shorter than we expect.
126: */
127: if (m->m_len < ip->ip_len) {
128: ipstat.ips_tooshort++;
129: goto bad;
130: }
131: /* Should drop packet if mbuf too long? hmmm... */
132: if (m->m_len > ip->ip_len)
133: m_adj(m, ip->ip_len - m->m_len);
134:
135: /* check ip_ttl for a correct ICMP reply */
136: if(ip->ip_ttl==0 || ip->ip_ttl==1) {
137: icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
138: goto bad;
139: }
140:
141: /*
142: * Process options and, if not destined for us,
143: * ship it on. ip_dooptions returns 1 when an
144: * error was detected (causing an icmp message
145: * to be sent and the original packet to be freed).
146: */
147: /* We do no IP options */
148: /* if (hlen > sizeof (struct ip) && ip_dooptions(m))
149: * goto next;
150: */
151: /*
152: * If offset or IP_MF are set, must reassemble.
153: * Otherwise, nothing need be done.
154: * (We could look in the reassembly queue to see
155: * if the packet was previously fragmented,
156: * but it's not worth the time; just let them time out.)
157: *
158: * XXX This should fail, don't fragment yet
159: */
160: if (ip->ip_off &~ IP_DF) {
161: register struct ipq *fp;
162: /*
163: * Look for queue of fragments
164: * of this datagram.
165: */
166: for (fp = (struct ipq *) ipq.next; fp != &ipq;
167: fp = (struct ipq *) fp->next)
168: if (ip->ip_id == fp->ipq_id &&
169: ip->ip_src.s_addr == fp->ipq_src.s_addr &&
170: ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
171: ip->ip_p == fp->ipq_p)
172: goto found;
173: fp = 0;
174: found:
175:
176: /*
177: * Adjust ip_len to not reflect header,
178: * set ip_mff if more fragments are expected,
179: * convert offset of this to bytes.
180: */
181: ip->ip_len -= hlen;
182: if (ip->ip_off & IP_MF)
183: ((struct ipasfrag *)ip)->ipf_mff |= 1;
184: else
185: ((struct ipasfrag *)ip)->ipf_mff &= ~1;
186:
187: ip->ip_off <<= 3;
188:
189: /*
190: * If datagram marked as having more fragments
191: * or if this is not the first fragment,
192: * attempt reassembly; if it succeeds, proceed.
193: */
194: if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
195: ipstat.ips_fragments++;
196: ip = ip_reass((struct ipasfrag *)ip, fp);
197: if (ip == 0)
198: return;
199: ipstat.ips_reassembled++;
200: m = dtom(ip);
201: } else
202: if (fp)
203: ip_freef(fp);
204:
205: } else
206: ip->ip_len -= hlen;
207:
208: /*
209: * Switch out to protocol's input routine.
210: */
211: ipstat.ips_delivered++;
212: switch (ip->ip_p) {
213: case IPPROTO_TCP:
214: tcp_input(m, hlen, (struct socket *)NULL);
215: break;
216: case IPPROTO_UDP:
217: udp_input(m, hlen);
218: break;
219: case IPPROTO_ICMP:
220: icmp_input(m, hlen);
221: break;
222: default:
223: ipstat.ips_noproto++;
224: m_free(m);
225: }
226: return;
227: bad:
228: m_freem(m);
229: return;
230: }
231:
232: /*
233: * Take incoming datagram fragment and try to
234: * reassemble it into whole datagram. If a chain for
235: * reassembly of this datagram already exists, then it
236: * is given as fp; otherwise have to make a chain.
237: */
238: struct ip *
239: ip_reass(ip, fp)
240: register struct ipasfrag *ip;
241: register struct ipq *fp;
242: {
243: register struct mbuf *m = dtom(ip);
244: register struct ipasfrag *q;
245: int hlen = ip->ip_hl << 2;
246: int i, next;
247:
248: DEBUG_CALL("ip_reass");
249: DEBUG_ARG("ip = %lx", (long)ip);
250: DEBUG_ARG("fp = %lx", (long)fp);
251: DEBUG_ARG("m = %lx", (long)m);
252:
253: /*
254: * Presence of header sizes in mbufs
255: * would confuse code below.
256: * Fragment m_data is concatenated.
257: */
258: m->m_data += hlen;
259: m->m_len -= hlen;
260:
261: /*
262: * If first fragment to arrive, create a reassembly queue.
263: */
264: if (fp == 0) {
265: struct mbuf *t;
266: if ((t = m_get()) == NULL) goto dropfrag;
267: fp = mtod(t, struct ipq *);
268: insque_32(fp, &ipq);
269: fp->ipq_ttl = IPFRAGTTL;
270: fp->ipq_p = ip->ip_p;
271: fp->ipq_id = ip->ip_id;
272: fp->ipq_next = fp->ipq_prev = (ipasfragp_32)fp;
273: fp->ipq_src = ((struct ip *)ip)->ip_src;
274: fp->ipq_dst = ((struct ip *)ip)->ip_dst;
275: q = (struct ipasfrag *)fp;
276: goto insert;
277: }
278:
279: /*
280: * Find a segment which begins after this one does.
281: */
282: for (q = (struct ipasfrag *)fp->ipq_next; q != (struct ipasfrag *)fp;
283: q = (struct ipasfrag *)q->ipf_next)
284: if (q->ip_off > ip->ip_off)
285: break;
286:
287: /*
288: * If there is a preceding segment, it may provide some of
289: * our data already. If so, drop the data from the incoming
290: * segment. If it provides all of our data, drop us.
291: */
292: if (q->ipf_prev != (ipasfragp_32)fp) {
293: i = ((struct ipasfrag *)(q->ipf_prev))->ip_off +
294: ((struct ipasfrag *)(q->ipf_prev))->ip_len - ip->ip_off;
295: if (i > 0) {
296: if (i >= ip->ip_len)
297: goto dropfrag;
298: m_adj(dtom(ip), i);
299: ip->ip_off += i;
300: ip->ip_len -= i;
301: }
302: }
303:
304: /*
305: * While we overlap succeeding segments trim them or,
306: * if they are completely covered, dequeue them.
307: */
308: while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
309: i = (ip->ip_off + ip->ip_len) - q->ip_off;
310: if (i < q->ip_len) {
311: q->ip_len -= i;
312: q->ip_off += i;
313: m_adj(dtom(q), i);
314: break;
315: }
316: q = (struct ipasfrag *) q->ipf_next;
317: m_freem(dtom((struct ipasfrag *) q->ipf_prev));
318: ip_deq((struct ipasfrag *) q->ipf_prev);
319: }
320:
321: insert:
322: /*
323: * Stick new segment in its place;
324: * check for complete reassembly.
325: */
326: ip_enq(ip, (struct ipasfrag *) q->ipf_prev);
327: next = 0;
328: for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp;
329: q = (struct ipasfrag *) q->ipf_next) {
330: if (q->ip_off != next)
331: return (0);
332: next += q->ip_len;
333: }
334: if (((struct ipasfrag *)(q->ipf_prev))->ipf_mff & 1)
335: return (0);
336:
337: /*
338: * Reassembly is complete; concatenate fragments.
339: */
340: q = (struct ipasfrag *) fp->ipq_next;
341: m = dtom(q);
342:
343: q = (struct ipasfrag *) q->ipf_next;
344: while (q != (struct ipasfrag *)fp) {
345: struct mbuf *t;
346: t = dtom(q);
347: q = (struct ipasfrag *) q->ipf_next;
1.1.1.2 ! root 348: m_cat(m, t);
1.1 root 349: }
350:
351: /*
352: * Create header for new ip packet by
353: * modifying header of first packet;
354: * dequeue and discard fragment reassembly header.
355: * Make header visible.
356: */
357: ip = (struct ipasfrag *) fp->ipq_next;
358:
359: /*
360: * If the fragments concatenated to an mbuf that's
361: * bigger than the total size of the fragment, then and
362: * m_ext buffer was alloced. But fp->ipq_next points to
363: * the old buffer (in the mbuf), so we must point ip
364: * into the new buffer.
365: */
366: if (m->m_flags & M_EXT) {
367: int delta;
368: delta = (char *)ip - m->m_dat;
369: ip = (struct ipasfrag *)(m->m_ext + delta);
370: }
371:
372: /* DEBUG_ARG("ip = %lx", (long)ip);
373: * ip=(struct ipasfrag *)m->m_data; */
374:
375: ip->ip_len = next;
376: ip->ipf_mff &= ~1;
377: ((struct ip *)ip)->ip_src = fp->ipq_src;
378: ((struct ip *)ip)->ip_dst = fp->ipq_dst;
379: remque_32(fp);
380: (void) m_free(dtom(fp));
381: m = dtom(ip);
382: m->m_len += (ip->ip_hl << 2);
383: m->m_data -= (ip->ip_hl << 2);
384:
385: return ((struct ip *)ip);
386:
387: dropfrag:
388: ipstat.ips_fragdropped++;
389: m_freem(m);
390: return (0);
391: }
392:
393: /*
394: * Free a fragment reassembly header and all
395: * associated datagrams.
396: */
397: void
398: ip_freef(fp)
399: struct ipq *fp;
400: {
401: register struct ipasfrag *q, *p;
402:
403: for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp;
404: q = p) {
405: p = (struct ipasfrag *) q->ipf_next;
406: ip_deq(q);
407: m_freem(dtom(q));
408: }
409: remque_32(fp);
410: (void) m_free(dtom(fp));
411: }
412:
413: /*
414: * Put an ip fragment on a reassembly chain.
415: * Like insque, but pointers in middle of structure.
416: */
417: void
418: ip_enq(p, prev)
419: register struct ipasfrag *p, *prev;
420: {
421: DEBUG_CALL("ip_enq");
422: DEBUG_ARG("prev = %lx", (long)prev);
423: p->ipf_prev = (ipasfragp_32) prev;
424: p->ipf_next = prev->ipf_next;
425: ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = (ipasfragp_32) p;
426: prev->ipf_next = (ipasfragp_32) p;
427: }
428:
429: /*
430: * To ip_enq as remque is to insque.
431: */
432: void
433: ip_deq(p)
434: register struct ipasfrag *p;
435: {
436: ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
437: ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
438: }
439:
440: /*
441: * IP timer processing;
442: * if a timer expires on a reassembly
443: * queue, discard it.
444: */
445: void
446: ip_slowtimo()
447: {
448: register struct ipq *fp;
449:
450: DEBUG_CALL("ip_slowtimo");
451:
452: fp = (struct ipq *) ipq.next;
453: if (fp == 0)
454: return;
455:
456: while (fp != &ipq) {
457: --fp->ipq_ttl;
458: fp = (struct ipq *) fp->next;
459: if (((struct ipq *)(fp->prev))->ipq_ttl == 0) {
460: ipstat.ips_fragtimeout++;
461: ip_freef((struct ipq *) fp->prev);
462: }
463: }
464: }
465:
466: /*
467: * Do option processing on a datagram,
468: * possibly discarding it if bad options are encountered,
469: * or forwarding it if source-routed.
470: * Returns 1 if packet has been forwarded/freed,
471: * 0 if the packet should be processed further.
472: */
473:
474: #ifdef notdef
475:
476: int
477: ip_dooptions(m)
478: struct mbuf *m;
479: {
480: register struct ip *ip = mtod(m, struct ip *);
481: register u_char *cp;
482: register struct ip_timestamp *ipt;
483: register struct in_ifaddr *ia;
484: /* int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */
485: int opt, optlen, cnt, off, code, type, forward = 0;
486: struct in_addr *sin, dst;
487: typedef u_int32_t n_time;
488: n_time ntime;
489:
490: dst = ip->ip_dst;
491: cp = (u_char *)(ip + 1);
492: cnt = (ip->ip_hl << 2) - sizeof (struct ip);
493: for (; cnt > 0; cnt -= optlen, cp += optlen) {
494: opt = cp[IPOPT_OPTVAL];
495: if (opt == IPOPT_EOL)
496: break;
497: if (opt == IPOPT_NOP)
498: optlen = 1;
499: else {
500: optlen = cp[IPOPT_OLEN];
501: if (optlen <= 0 || optlen > cnt) {
502: code = &cp[IPOPT_OLEN] - (u_char *)ip;
503: goto bad;
504: }
505: }
506: switch (opt) {
507:
508: default:
509: break;
510:
511: /*
512: * Source routing with record.
513: * Find interface with current destination address.
514: * If none on this machine then drop if strictly routed,
515: * or do nothing if loosely routed.
516: * Record interface address and bring up next address
517: * component. If strictly routed make sure next
518: * address is on directly accessible net.
519: */
520: case IPOPT_LSRR:
521: case IPOPT_SSRR:
522: if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
523: code = &cp[IPOPT_OFFSET] - (u_char *)ip;
524: goto bad;
525: }
526: ipaddr.sin_addr = ip->ip_dst;
527: ia = (struct in_ifaddr *)
528: ifa_ifwithaddr((struct sockaddr *)&ipaddr);
529: if (ia == 0) {
530: if (opt == IPOPT_SSRR) {
531: type = ICMP_UNREACH;
532: code = ICMP_UNREACH_SRCFAIL;
533: goto bad;
534: }
535: /*
536: * Loose routing, and not at next destination
537: * yet; nothing to do except forward.
538: */
539: break;
540: }
541: off--; / * 0 origin * /
542: if (off > optlen - sizeof(struct in_addr)) {
543: /*
544: * End of source route. Should be for us.
545: */
546: save_rte(cp, ip->ip_src);
547: break;
548: }
549: /*
550: * locate outgoing interface
551: */
552: bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
553: sizeof(ipaddr.sin_addr));
554: if (opt == IPOPT_SSRR) {
555: #define INA struct in_ifaddr *
556: #define SA struct sockaddr *
557: if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
558: ia = (INA)ifa_ifwithnet((SA)&ipaddr);
559: } else
560: ia = ip_rtaddr(ipaddr.sin_addr);
561: if (ia == 0) {
562: type = ICMP_UNREACH;
563: code = ICMP_UNREACH_SRCFAIL;
564: goto bad;
565: }
566: ip->ip_dst = ipaddr.sin_addr;
567: bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
568: (caddr_t)(cp + off), sizeof(struct in_addr));
569: cp[IPOPT_OFFSET] += sizeof(struct in_addr);
570: /*
571: * Let ip_intr's mcast routing check handle mcast pkts
572: */
573: forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
574: break;
575:
576: case IPOPT_RR:
577: if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
578: code = &cp[IPOPT_OFFSET] - (u_char *)ip;
579: goto bad;
580: }
581: /*
582: * If no space remains, ignore.
583: */
584: off--; * 0 origin *
585: if (off > optlen - sizeof(struct in_addr))
586: break;
587: bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
588: sizeof(ipaddr.sin_addr));
589: /*
590: * locate outgoing interface; if we're the destination,
591: * use the incoming interface (should be same).
592: */
593: if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
594: (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
595: type = ICMP_UNREACH;
596: code = ICMP_UNREACH_HOST;
597: goto bad;
598: }
599: bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
600: (caddr_t)(cp + off), sizeof(struct in_addr));
601: cp[IPOPT_OFFSET] += sizeof(struct in_addr);
602: break;
603:
604: case IPOPT_TS:
605: code = cp - (u_char *)ip;
606: ipt = (struct ip_timestamp *)cp;
607: if (ipt->ipt_len < 5)
608: goto bad;
609: if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
610: if (++ipt->ipt_oflw == 0)
611: goto bad;
612: break;
613: }
614: sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
615: switch (ipt->ipt_flg) {
616:
617: case IPOPT_TS_TSONLY:
618: break;
619:
620: case IPOPT_TS_TSANDADDR:
621: if (ipt->ipt_ptr + sizeof(n_time) +
622: sizeof(struct in_addr) > ipt->ipt_len)
623: goto bad;
624: ipaddr.sin_addr = dst;
625: ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
626: m->m_pkthdr.rcvif);
627: if (ia == 0)
628: continue;
629: bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
630: (caddr_t)sin, sizeof(struct in_addr));
631: ipt->ipt_ptr += sizeof(struct in_addr);
632: break;
633:
634: case IPOPT_TS_PRESPEC:
635: if (ipt->ipt_ptr + sizeof(n_time) +
636: sizeof(struct in_addr) > ipt->ipt_len)
637: goto bad;
638: bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
639: sizeof(struct in_addr));
640: if (ifa_ifwithaddr((SA)&ipaddr) == 0)
641: continue;
642: ipt->ipt_ptr += sizeof(struct in_addr);
643: break;
644:
645: default:
646: goto bad;
647: }
648: ntime = iptime();
649: bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
650: sizeof(n_time));
651: ipt->ipt_ptr += sizeof(n_time);
652: }
653: }
654: if (forward) {
655: ip_forward(m, 1);
656: return (1);
657: }
658: }
659: }
660: return (0);
661: bad:
662: /* ip->ip_len -= ip->ip_hl << 2; XXX icmp_error adds in hdr length */
663:
664: /* Not yet */
665: icmp_error(m, type, code, 0, 0);
666:
667: ipstat.ips_badoptions++;
668: return (1);
669: }
670:
671: #endif /* notdef */
672:
673: /*
674: * Strip out IP options, at higher
675: * level protocol in the kernel.
676: * Second argument is buffer to which options
677: * will be moved, and return value is their length.
678: * (XXX) should be deleted; last arg currently ignored.
679: */
680: void
681: ip_stripoptions(m, mopt)
682: register struct mbuf *m;
683: struct mbuf *mopt;
684: {
685: register int i;
686: struct ip *ip = mtod(m, struct ip *);
687: register caddr_t opts;
688: int olen;
689:
690: olen = (ip->ip_hl<<2) - sizeof (struct ip);
691: opts = (caddr_t)(ip + 1);
692: i = m->m_len - (sizeof (struct ip) + olen);
693: memcpy(opts, opts + olen, (unsigned)i);
694: m->m_len -= olen;
695:
696: ip->ip_hl = sizeof(struct ip) >> 2;
697: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.