|
|
1.1 ! root 1: .TH ENET 4 "17 January 1986" Stanford ! 2: .SH "NAME" ! 3: enet \- ethernet packet filter ! 4: .SH SYNOPSIS ! 5: .B "pseudo-device enetfilter 64" ! 6: .SH "DESCRIPTION" ! 7: The packet filter ! 8: provides a raw interface to Ethernets and similar network data link layers. ! 9: Packets received that are not used by the kernel ! 10: (i.e., to support IP, ARP, and on some systems XNS, protocols) ! 11: are available through this mechanism. ! 12: The packet filter appears as a set of character special files, one ! 13: per hardware interface. ! 14: Each ! 15: .I enet ! 16: file may be opened multiple times, allowing each interface to be used by ! 17: many processes. ! 18: The total number of open ethernet ! 19: files is limited to the value given in the kernel configuration; the ! 20: example given in the SYNOPSIS above sets the limit to 64. ! 21: .PP ! 22: The minor device numbers ! 23: are associated with interfaces when the system is booted. ! 24: Minor device 0 ! 25: is associated with the first Ethernet interface ``attached'', ! 26: minor device 1 with the second, and so forth. ! 27: (These character special files are, for historical reasons, ! 28: given the names ! 29: .IR /dev/enet0 , ! 30: .IR /dev/eneta0 , ! 31: .IR /dev/enetb0 , ! 32: etc.) ! 33: .PP ! 34: Associated with each open instance of an ! 35: .I enet ! 36: file is a user-settable packet filter which is used to deliver ! 37: incoming ethernet packets to the appropriate process. ! 38: Whenever a packet is received from the net, ! 39: successive packet filters from the list of filters for ! 40: all open ! 41: .I enet ! 42: files are applied to the packet. ! 43: When a filter accepts the packet, ! 44: it is placed on the packet input queue of the ! 45: associated file. ! 46: If no filters accept the packet, it is discarded. ! 47: The format of a packet filter is described below. ! 48: .PP ! 49: Reads from these files return the next packet ! 50: from a queue of packets that have matched the filter. ! 51: If insufficient buffer space to store the entire packet is specified in the ! 52: read, the packet will be truncated and the trailing contents lost. ! 53: Writes to these devices transmit packets on the ! 54: network, with each write generating exactly one packet. ! 55: .PP ! 56: The packet filter currently supports a variety of different ``Ethernet'' ! 57: data-link levels: ! 58: .IP "3mb Ethernet" 1.5i ! 59: packets consist of 4 or more bytes with the first byte ! 60: specifying the source ethernet address, the second ! 61: byte specifying the destination ethernet address, ! 62: and the next two bytes specifying the packet type. ! 63: (Actually, on the network the source and destination addresses ! 64: are in the opposite order.) ! 65: .IP "byte-swapping 3mb Ethernet" 1.5i ! 66: packets consist of 4 or more bytes with the first byte ! 67: specifying the source ethernet address, the second ! 68: byte specifying the destination ethernet address, ! 69: and the next two bytes specifying the packet type. ! 70: \fBEach short word (pair of bytes) is swapped from the network ! 71: byte order\fR; this device type is only provided as a concession ! 72: to backwards-compatibility. ! 73: .IP "10mb Ethernet" 1.5i ! 74: packets consist of 14 or more bytes with the first six ! 75: bytes specifying the destination ethernet address, ! 76: the next six bytes the source ethernet address, ! 77: and the next two bytes specifying the packet type. ! 78: .PP ! 79: The remaining words are interpreted according to the packet type. ! 80: Note that 16-bit and 32-bit quantities may have to be byteswapped ! 81: (and possible short-swapped) to be intelligible on a Vax. ! 82: .PP ! 83: The packet filter mechanism does not know anything about the data portion ! 84: of the packets it sends and receives. The user must supply ! 85: the headers for transmitted packets (although the system makes sure that ! 86: the source address is correct) and the headers of received packets ! 87: are delivered to the user. The packet filters treat the entire packet, ! 88: including headers, as uninterpreted data. ! 89: .SH "IOCTL CALLS" ! 90: In addition to FIONREAD, ! 91: ten special ! 92: .I ! 93: ioctl ! 94: calls may be applied to an open ! 95: .I ! 96: enet ! 97: file. ! 98: The first two set and fetch parameters ! 99: for the file and are of the form: ! 100: .sp ! 101: .nf ! 102: .RS ! 103: .B #include <sys/types.h> ! 104: .B #include <sys/enet.h> ! 105: .B ioctl(fildes, code, param) ! 106: .B ! 107: struct eniocb *param; ! 108: .RE ! 109: .fi ! 110: .sp ! 111: where ! 112: .I ! 113: param ! 114: is defined in ! 115: .I ! 116: <sys/enet.h> ! 117: as: ! 118: .br ! 119: .sp ! 120: .nf ! 121: .RS ! 122: .ta \w'struct 'u +\w'u_char 'u ! 123: .ft B ! 124: struct eniocb ! 125: { ! 126: u_char en_addr; ! 127: u_char en_maxfilters; ! 128: u_char en_maxwaiting; ! 129: u_char en_maxpriority; ! 130: long en_rtout; ! 131: }; ! 132: .DT ! 133: .RE ! 134: .fi ! 135: .ft R ! 136: .sp ! 137: with the applicable codes being: ! 138: .TP ! 139: EIOCGETP ! 140: Fetch the parameters for this file. ! 141: .TP ! 142: EIOCSETP ! 143: Set the parameters for this file. ! 144: .i0 ! 145: .DT ! 146: .PP ! 147: The maximum filter length parameter en_maxfilters indicates ! 148: the maximum possible packet filter command ! 149: list length (see EIOCSETF below). ! 150: The maximum input wait queue size parameter en_maxwaitingindicates ! 151: the maximum number of packets which may be queued for an ! 152: ethernet file at one time (see EIOCSETW below). ! 153: The maximum priority parameter en_maxpriority indicates the highest ! 154: filter priority which may be set for the file (see EIOCSETF below). ! 155: The en_addr field is no longer maintained by the driver; see ! 156: EIOCDEVP below. ! 157: .PP ! 158: The read timeout parameter en_rtout specifies the number of clock ticks to ! 159: wait before timing out on a read request and returning an EOF. ! 160: This parameter is initialized to zero by ! 161: .IR open (2), ! 162: indicating no timeout. If it is negative, then read requests will return an ! 163: EOF immediately if there are no packets in the input queue. ! 164: (Note that all parameters except for the read timeout are read-only ! 165: and are ignored when changed.) ! 166: .PP ! 167: A different ! 168: .I ioctl ! 169: is used to get device parameters of the ethernet underlying the ! 170: minor device. It is of the form: ! 171: .sp ! 172: .nf ! 173: .RS ! 174: .B #include <sys/types.h> ! 175: .br ! 176: .B #include <sys/enet.h> ! 177: .B ioctl(fildes, EIOCDEVP, param) ! 178: .RE ! 179: .fi ! 180: .sp ! 181: where ! 182: .I param ! 183: is defined in ! 184: .I <sys/enet.h> ! 185: as: ! 186: .br ! 187: .sp ! 188: .nf ! 189: .RS ! 190: .ta \w'struct 'u +\w'u_short 'u ! 191: .ft B ! 192: struct endevp { ! 193: u_char end_dev_type; ! 194: u_char end_addr_len; ! 195: u_short end_hdr_len; ! 196: u_short end_MTU; ! 197: u_char end_addr[EN_MAX_ADDR_LEN]; ! 198: u_char end_broadaddr[EN_MAX_ADDR_LEN]; ! 199: }; ! 200: .DT ! 201: .RE ! 202: .fi ! 203: .ft R ! 204: .sp ! 205: The fields are: ! 206: .IP end_dev_type 1.5i ! 207: Specifies the device type; currently one of ENDT_3MB, ENDT_BS3MB or ENDT_10MB. ! 208: .IP end_addr_len 1.5i ! 209: Specifies the address length in bytes (e.g., 1 or 6). ! 210: .IP end_hdr_len 1.5i ! 211: Specifies the total header length in bytes (e.g., 4 or 14). ! 212: .IP end_MTU 1.5i ! 213: Specifies the maximum packet size, including header, in bytes. ! 214: .IP end_addr 1.5i ! 215: The address of this interface; aligned so that the low order ! 216: byte of the address is the first byte in the array. ! 217: .IP end_broadaddr 1.5i ! 218: The hardware destination address for broadcasts on this network. ! 219: .PP ! 220: The next two calls enable and disable the input ! 221: packet signal mechanism ! 222: for the file and are of the form: ! 223: .sp ! 224: .nf ! 225: .RS ! 226: .B #include <sys/types.h> ! 227: .B #include <sys/enet.h> ! 228: .B ioctl(fildes, code, signp) ! 229: .B ! 230: u_int *signp; ! 231: .RE ! 232: .fi ! 233: .sp ! 234: where ! 235: .I ! 236: signp ! 237: is a pointer to a word containing the number ! 238: of the signal ! 239: to be sent when an input packet arrives and ! 240: with the applicable codes being: ! 241: .TP ! 242: EIOCENBS ! 243: Enable the specified signal when an input packet ! 244: is received for this file. ! 245: If the ENHOLDSIG flag (see EIOCMBIS below) is not set, ! 246: further signals are automatically disabled ! 247: whenever a signal is sent to prevent nesting and hence ! 248: must be specifically re-enabled after processing. ! 249: When a signal number of 0 is supplied, ! 250: this call is equivalent to EIOCINHS. ! 251: .TP ! 252: EIOCINHS ! 253: Disable any signal when an input ! 254: packet is received for this file ! 255: (the ! 256: .I signp ! 257: parameter is ignored). ! 258: This is the default when the file is first opened. ! 259: .i0 ! 260: .DT ! 261: .PP ! 262: .sp ! 263: The next two calls set and clear ``mode bits'' for the ! 264: for the file and are of the form: ! 265: .sp ! 266: .nf ! 267: .RS ! 268: .B #include <sys/types.h> ! 269: .B #include <sys/enet.h> ! 270: .B ioctl(fildes, code, bits) ! 271: .B ! 272: u_short *bits; ! 273: .RE ! 274: .fi ! 275: .sp ! 276: where ! 277: .I bits ! 278: is a short work bit-mask specifying which bits to set or clear. ! 279: Currently, the only bit mask recognized is ENHOLDSIG, which (if ! 280: .IR clear ) ! 281: means that the driver should ! 282: disable the effect of EIOCENBS once it has delivered a signal. ! 283: Setting this bit ! 284: means that you need use EIOCENBS only once. (For historical reasons, the ! 285: default is that ENHOLDSIG is set.) ! 286: The applicable codes are: ! 287: .TP ! 288: EIOCMBIS ! 289: Sets the specified mode bits ! 290: .TP ! 291: EIOCMBIC ! 292: Clears the specified mode bits ! 293: .PP ! 294: Another ! 295: .I ! 296: ioctl ! 297: call is used to set the maximum size ! 298: of the packet input queue for ! 299: an open ! 300: .I ! 301: enet ! 302: file. ! 303: It is of the form: ! 304: .sp ! 305: .nf ! 306: .RS ! 307: .B #include <sys/types.h> ! 308: .B #include <sys/enet.h> ! 309: .B ioctl(fildes, EIOCSETW, maxwaitingp) ! 310: .B u_int *maxwaitingp; ! 311: .RE ! 312: .fi ! 313: .sp ! 314: where ! 315: .I ! 316: maxwaitingp ! 317: is a pointer ! 318: to a word containing ! 319: the input queue size to be set. ! 320: If this is greater than maximum allowable ! 321: size (see EIOCGETP above), it is set to the maximum, ! 322: and if it is zero, it is set to ! 323: a default value. ! 324: .sp ! 325: Another ! 326: .I ! 327: ioctl ! 328: call flushes the queue of incoming packets. ! 329: It is of the ! 330: form: ! 331: .sp ! 332: .nf ! 333: .RS ! 334: .B #include <sys/types.h> ! 335: .B #include <sys/enet.h> ! 336: .B ioctl(fildes, EIOCFLUSH, 0) ! 337: .RE ! 338: .fi ! 339: .sp ! 340: The final ! 341: .I ! 342: ioctl ! 343: call is used to set the packet filter ! 344: for an open ! 345: .I ! 346: enet ! 347: file. ! 348: It is of the form: ! 349: .sp ! 350: .nf ! 351: .RS ! 352: .B #include <sys/types.h> ! 353: .B #include <sys/enet.h> ! 354: .B ioctl(fildes, EIOCSETF, filter) ! 355: .B struct enfilter *filter ! 356: .RE ! 357: .fi ! 358: .sp ! 359: where enfilter is defined in ! 360: .I ! 361: <sys/enet.h> ! 362: as: ! 363: .sp ! 364: .nf ! 365: .RS ! 366: .ft B ! 367: .ta \w'struct 'u \w'struct u_short 'u ! 368: struct enfilter ! 369: { ! 370: u_char enf_Priority; ! 371: u_char enf_FilterLen; ! 372: u_short enf_Filter[ENMAXFILTERS]; ! 373: }; ! 374: .DT ! 375: .ft R ! 376: .RE ! 377: .fi ! 378: .PP ! 379: A packet filter consists of a priority, ! 380: the filter command list length (in shortwords), ! 381: and the filter command list itself. ! 382: Each filter command list specifies ! 383: a sequence of actions which ! 384: operate on an internal stack. ! 385: Each shortword of the ! 386: command list specifies an action from the set { ! 387: .B ! 388: ENF_PUSHLIT, ! 389: .B ! 390: ENF_PUSHZERO, ! 391: .B ! 392: ENF_PUSHWORD+N ! 393: } which respectively push the next shortword of the ! 394: command list, zero, ! 395: or shortword ! 396: .B ! 397: N ! 398: of the incoming packet on the stack, and a binary operator ! 399: from the set { ! 400: .B ! 401: ENF_EQ, ! 402: .B ! 403: ENF_NEQ, ! 404: .B ! 405: ENF_LT, ! 406: .B ! 407: ENF_LE, ! 408: .B ! 409: ENF_GT, ! 410: .B ! 411: ENF_GE, ! 412: .B ! 413: ENF_AND, ! 414: .B ! 415: ENF_OR, ! 416: .B ! 417: ENF_XOR ! 418: } ! 419: which then operates on the ! 420: top two elements of the stack and replaces them with its result. ! 421: When both an action and operator are specified in the ! 422: same shortword, the action is performed followed by the ! 423: operation. ! 424: .PP ! 425: The binary operator can also be from the set { ! 426: .B ! 427: ENF_COR, ! 428: .B ! 429: ENF_CAND, ! 430: .B ! 431: ENF_CNOR, ! 432: .B ! 433: ENF_CNAND ! 434: }. These are ``short-circuit'' operators, in that they terminate ! 435: the execution of the filter immediately if the condition they are checking ! 436: for is found, and continue otherwise. ! 437: All pop two elements from the stack and compare them for equality; ! 438: .B ! 439: ENF_CAND ! 440: returns false if the result is false; ! 441: .B ! 442: ENF_COR ! 443: returns true if the result is true; ! 444: .B ! 445: ENF_CNAND ! 446: returns true if the result is false; ! 447: .B ! 448: ENF_CNOR ! 449: returns false if the result is true. ! 450: Unlike the other binary operators, these four do not leave a result ! 451: on the stack, even if they continue. ! 452: .PP ! 453: The short-circuit operators should be used when possible, to reduce the ! 454: amount of time spent evaluating filters. When they are used, you should ! 455: also arrange the order of the tests so that the filter will succeed or fail ! 456: as soon as possible; for example, checking the Socket field of a Pup packet ! 457: is more likely to indicate failure than the packet type field. ! 458: .PP ! 459: The ! 460: special action ! 461: .B ! 462: ENF_NOPUSH ! 463: and the special operator ! 464: .B ! 465: ENF_NOP ! 466: can be used to only ! 467: perform the binary operation or to only push a value on the stack. ! 468: Since both are (conveniently) defined to be zero, indicating ! 469: only an action actually specifies the action followed by ! 470: .BR ENF_NOP , ! 471: and ! 472: indicating only an operation actually specifies ! 473: .B ! 474: ENF_NOPUSH ! 475: followed ! 476: by the operation. ! 477: .PP ! 478: After executing the filter command list, a non-zero value (true) ! 479: left on top of the stack ! 480: (or an empty stack) causes the incoming ! 481: packet to be accepted for the corresponding ! 482: .I ! 483: enet ! 484: file and a zero value (false) causes the packet to ! 485: be passed through the next packet filter. ! 486: (If the filter exits as the result of a short-circuit operator, ! 487: the top-of-stack value is ignored.) ! 488: Specifying an undefined operation or action in the command list ! 489: or performing an illegal operation or action (such as pushing ! 490: a shortword offset ! 491: past the end of the packet or executing a binary operator ! 492: with fewer than two shortwords on the stack) causes a filter to ! 493: reject the packet. ! 494: .sp ! 495: In an attempt to deal with the problem of ! 496: overlapping and/or conflicting packet filters, ! 497: the filters for each open ! 498: .I ! 499: enet ! 500: file are ordered by the driver ! 501: according to their priority ! 502: (lowest ! 503: priority is 0, highest is 255). ! 504: When processing incoming ! 505: ethernet ! 506: packets, filters are applied according to their ! 507: priority (from highest to lowest) and ! 508: for identical priority values according to their ! 509: relative ``busyness'' (the filter that has previously ! 510: matched the most packets is checked first) until one or more filters ! 511: accept the packet or all filters reject it and ! 512: it is discarded. ! 513: .PP ! 514: Filters at a priority of 2 or higher are called "high priority" ! 515: filters. ! 516: Once a packet is delivered to one of these "high priority" ! 517: .I ! 518: enet ! 519: files, ! 520: no further filters are examined, ! 521: i.e. ! 522: the packet is delivered only ! 523: to the first ! 524: .I ! 525: enet ! 526: file with a ! 527: "high priority" filter ! 528: which accepts the packet. ! 529: A packet may be delivered to more than one filter with a priority ! 530: below 2; this might be useful, for example, in building replicated programs. ! 531: However, the use of low-priority filters imposes an additional cost on ! 532: the system, as these filters each must be checked against all packets not ! 533: accepted by a high-priority filter. ! 534: .PP ! 535: The packet filter for an ! 536: .I ! 537: enet ! 538: file is initialized ! 539: with length 0 at priority 0 by ! 540: .IR open (2), ! 541: and hence by default accepts all ! 542: packets which no "high priority" filter ! 543: is interested in. ! 544: .PP ! 545: Priorities should be assigned so that, in general, the more packets a ! 546: filter is expected to match, the higher its priority. This will prevent ! 547: a lot of needless checking of packets against filters that aren't likely ! 548: to match them. ! 549: .SH "FILTER EXAMPLES" ! 550: The following filter would accept all incoming ! 551: .I Pup ! 552: packets on a 3mb ethernet with Pup types in the range 1-0100: ! 553: .sp ! 554: .nf ! 555: .ft B ! 556: .ta \w'stru'u \w'struct ENF_PUSHWORD+8, ENF_PUSHLIT, 2, 'u ! 557: struct enfilter f = ! 558: { ! 559: 10, 19, /* priority and length */ ! 560: ENF_PUSHWORD+1, ENF_PUSHLIT, 2, ! 561: ENF_EQ, /* packet type == PUP */ ! 562: ENF_PUSHWORD+3, ENF_PUSHLIT, ! 563: 0xFF00, ENF_AND, /* mask high byte */ ! 564: ENF_PUSHZERO, ENF_GT, /* PupType > 0 */ ! 565: ENF_PUSHWORD+3, ENF_PUSHLIT, ! 566: 0xFF00, ENF_AND, /* mask high byte */ ! 567: ENF_PUSHLIT, 0100, ENF_LE, /* PupType <= 0100 */ ! 568: ENF_AND, /* 0 < PupType <= 0100 */ ! 569: ENF_AND /* && packet type == PUP */ ! 570: }; ! 571: .DT ! 572: .ft R ! 573: .fi ! 574: .sp ! 575: Note that shortwords, such as the packet type field, are byte-swapped ! 576: and so the literals you compare them to must be byte-swapped. Also, ! 577: although for this example the word offsets are constants, code that ! 578: must run with either 3mb or 10mb ethernets must use ! 579: offsets that depend on the device type. ! 580: .PP ! 581: By taking advantage of the ability to ! 582: specify both an action and operation in each word of ! 583: the command list, the filter could be abbreviated to: ! 584: .sp ! 585: .nf ! 586: .ta \w'stru'u \w'struct ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, 'u ! 587: .ft B ! 588: struct enfilter f = ! 589: { ! 590: 10, 14, /* priority and length */ ! 591: ENF_PUSHWORD+1, ENF_PUSHLIT | ENF_EQ, 2, /* packet type == PUP */ ! 592: ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, ! 593: 0xFF00, /* mask high byte */ ! 594: ENF_PUSHZERO | ENF_GT, /* PupType > 0 */ ! 595: ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_AND, ! 596: 0xFF00, /* mask high byte */ ! 597: ENF_PUSHLIT | ENF_LE, 0100, /* PupType <= 0100 */ ! 598: ENF_AND, /* 0 < PupType <= 0100 */ ! 599: ENF_AND /* && packet type == PUP */ ! 600: }; ! 601: .ft R ! 602: .DT ! 603: .fi ! 604: .sp ! 605: A different example shows the use of "short-circuit" operators to ! 606: create a more efficient filter. This one accepts Pup packets (on a 3Mbit ! 607: ethernet) with a Socket field of 12345. Note that we check the Socket field ! 608: before the packet type field, since in most ! 609: packets the Socket is not likely to match. ! 610: .sp ! 611: .nf ! 612: .ta \w'stru'u \w'struct ENF_PUSHWORD+3, ENF_PUSHLIT | ENF_CAND, 'u ! 613: .ft B ! 614: struct enfilter f = ! 615: { ! 616: 10, 9, /* priority and length */ ! 617: ENF_PUSHWORD+7, ENF_PUSHLIT | ENF_CAND, ! 618: 0, /* High word of socket */ ! 619: ENF_PUSHWORD+8, ENF_PUSHLIT | ENF_CAND, ! 620: 12345, /* Low word of socket */ ! 621: ENF_PUSHWORD+1, ENF_PUSHLIT | ENF_CAND, ! 622: 2 /* packet type == Pup */ ! 623: }; ! 624: .ft R ! 625: .DT ! 626: .fi ! 627: .SH "SEE ALSO" ! 628: de(4), ec(4), en(4), il(4), enstat(8) ! 629: .SH "FILES" ! 630: /dev/enet{,a,b,c,...}0 ! 631: .SH BUGS ! 632: The current implementation can only filter on words within ! 633: the first "mbuf" of the packet; this is around 100 bytes (or ! 634: 50 words). ! 635: .PP ! 636: Because packets are streams of bytes, yet the filters operate ! 637: on short words, and standard network byte order is usually opposite ! 638: from Vax byte order, the relational operators ! 639: .B ENF_LT, ENF_LE, ! 640: .B ENF_GT, ! 641: and ! 642: .B ENF_GE ! 643: are not all that useful. Fortunately, they were not often used ! 644: when the packets were treated as streams of shorts, so this is ! 645: probably not a severe problem. If this becomes a severe problem, ! 646: a byte-swapping operator could be added. ! 647: .PP ! 648: Many of the "features" of this driver are there for historical ! 649: reasons; the manual page could be a lot cleaner if these were ! 650: left out. ! 651: .SH "HISTORY" ! 652: .TP ! 653: 8-Oct-85 Jeffrey Mogul at Stanford University ! 654: Revised to describe 4.3BSD version of driver. ! 655: .TP ! 656: 18-Oct-84 Jeffrey Mogul at Stanford University ! 657: Added short-circuit operators, changed discussion of priorities to ! 658: reflect new arrangement. ! 659: .TP ! 660: 18-Jan-84 Jeffrey Mogul at Stanford University ! 661: Updated for 4.2BSD (device-independent) version, including ! 662: documentation of all non-kernel ioctls. ! 663: .TP ! 664: 17-Nov-81 Mike Accetta (mja) at Carnegie-Mellon University ! 665: Added mention of <sys/types.h> to include examples. ! 666: .TP ! 667: 29-Sep-81 Mike Accetta (mja) at Carnegie-Mellon University ! 668: Changed to describe new EIOCSETW and EIOCFLUSH ioctl ! 669: calls and the new multiple packet queuing features. ! 670: .TP ! 671: 12-Nov-80 Mike Accetta (mja) at Carnegie-Mellon University ! 672: Added description of signal mechanism for input packets. ! 673: .TP ! 674: 07-Mar-80 Mike Accetta (mja) at Carnegie-Mellon University ! 675: Created.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.