The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ttd/ttd_comm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* 
    2  * Mach Operating System
    3  * Copyright (c) 1992 Carnegie Mellon University
    4  * All Rights Reserved.
    5  * 
    6  * Permission to use, copy, modify and distribute this software and its
    7  * documentation is hereby granted, provided that both the copyright
    8  * notice and this permission notice appear in all copies of the
    9  * software, derivative works or modified versions, and any portions
   10  * thereof, and that both notices appear in supporting documentation.
   11  * 
   12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   15  * 
   16  * Carnegie Mellon requests users of this software to return to
   17  * 
   18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   19  *  School of Computer Science
   20  *  Carnegie Mellon University
   21  *  Pittsburgh PA 15213-3890
   22  * 
   23  * any improvements or extensions that they make and grant Carnegie Mellon
   24  * the rights to redistribute these changes.
   25  */
   26 /*
   27  * TTD Communications parsing code for the kernel ttd server.
   28  *
   29  * HISTORY:
   30  * $Log:        ttd_comm.c,v $
   31  * Revision 2.2  93/05/10  23:24:28  rvb
   32  *      Checkin for MK80 branch.
   33  *      [93/05/10  15:05:52  grm]
   34  * 
   35  * Revision 2.1.2.2  93/04/20  10:51:43  grm
   36  *      Changed for use with mips and machines of both endian types.
   37  *      Changed many of the types so that the same protocol will be
   38  *      decodable by many machine types.
   39  *      [93/04/20            grm]
   40  * 
   41  * Revision 2.1.2.1  93/03/03  14:35:46  grm
   42  *      Second version of code.  It works.
   43  *      [93/03/03            grm]
   44  * 
   45  * Revision 2.1.1.10  93/01/22  15:51:24  grm
   46  *      Added request pkt length checks.
   47  * 
   48  * Revision 2.1.1.9  93/01/21  13:02:00  grm
   49  *      Changed to ansi prototypes.
   50  * 
   51  * Revision 2.1.1.8  92/10/23  21:16:53  grm
   52  *      Fixed bug in kttd_valid_request so that it looks at correct
   53  *      incoming packet.
   54  *      [92/10/23            grm]
   55  * 
   56  * Revision 2.1.1.7  92/10/08  14:27:10  grm
   57  *      Now sends and receives packets.
   58  *      [92/10/08            grm]
   59  * 
   60  * Revision 2.1.1.6  92/10/01  15:35:50  grm
   61  *      Restructuring of ttd code checkpoint.
   62  *      [92/10/01            grm]
   63  * 
   64  * Revision 2.1.1.5  92/09/30  13:32:31  grm
   65  *      Changed for use with Mach specific kttd routines (sync and async).
   66  *      Added get_request, and valid_request.
   67  *      [92/09/30            grm]
   68  * 
   69  * Revision 2.1.1.4  92/09/25  15:15:34  grm
   70  *      checkpointing...
   71  *      [92/09/25            grm]
   72  * 
   73  * Revision 2.1.1.3  92/09/21  13:22:17  grm
   74  *      This version uses bootp, and responds to arp requests.
   75  *      [92/09/21            grm]
   76  * 
   77  * Revision 2.1.1.2  92/09/15  18:27:57  grm
   78  *      Checkpoint version with bootp working.
   79  * 
   80  * Revision 2.1.1.1  92/09/09  14:44:07  grm
   81  *      Initial checkin.
   82  * 
   83  */
   84 /***********************************************************
   85 Copyright 1992 by Digital Equipment Corporation, Maynard, Massachusetts,
   86 
   87                         All Rights Reserved
   88 
   89 Permission to use, copy, modify, and distribute this software and its 
   90 documentation for any purpose and without fee is hereby granted, provided 
   91 that the above copyright notice appear in all copies and that both that 
   92 copyright notice and this permission notice appear in supporting 
   93 documentation, and that the name of Digital not be used in advertising 
   94 or publicity pertaining to distribution of the software without specific, 
   95 written prior permission.  Digital makes no representations about the 
   96 suitability of this software for any purpose.  It is provided "as is"
   97 without express or implied warranty.
   98 
   99 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  100 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  101 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  102 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  103 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  104 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  105 SOFTWARE.
  106 
  107 ******************************************************************/
  108 
  109 /*****************************************************/
  110 /*                    ttd_comm.c                     */
  111 /* Simple TTD/UDP/IP/ARP protocol package for lowttd */
  112 /*****************************************************/
  113 
  114 #include <device/if_ether.h>
  115 #include <device/net_status.h>
  116 #include <ttd/ttd_stub.h>
  117 #include <ttd/ttd_comm.h>
  118 #include <ttd/ttd_types.h>
  119 #include <ttd/ttd_msg.h>
  120 #include <ttd/ttd_debug.h>
  121 
  122 
  123 static struct ether_hardware_address broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
  124 
  125 static struct ip_address my_ip_addr;
  126 static struct ip_address ip_broadcast = {0xff, 0xff, 0xff, 0xff};
  127 static struct ip_address ip_undefined = {0x00, 0x00, 0x00, 0x00};
  128 
  129 #define skip_pkt_header(pkt)    (arp_packet_t)((natural_t)pkt + sizeof(struct packet_header))
  130 
  131 /*
  132  * My own damn byte swapping routines:
  133  */
  134 #if     BYTE_MSF
  135 #else
  136 u_short netswap_2_bytes(u_short n)
  137 {
  138         u_short tmp0;
  139 
  140         tmp0 = (n << 8);
  141         tmp0 |= (n >> 8);
  142                 
  143         return tmp0;
  144 }
  145 
  146 uint32 netswap_4_bytes(uint32 n)
  147 {
  148         uint32 tmp0, tmp1;
  149 
  150         tmp0 = (n << 24) | (n >> 24);
  151         tmp1 = (n & 0xff00) << 8;
  152         tmp0 |= tmp1;
  153         tmp1 = (n >> 8) & 0xff00;
  154         return tmp0 | tmp1;
  155 }
  156 #endif  /* BYTE_MSF */
  157 
  158 /***************************************/
  159 /*        Checksum routines            */
  160 /***************************************/
  161 
  162 /*
  163  * finish_checksum converts a two's complement accumulation of
  164  * HalfWords into the one's complement checksum.
  165  */
  166 static int finish_checksum(int w)
  167 {
  168         if (w == 0)
  169                 return 0xffff;
  170         while (w > 0xffff)
  171                 w = (w & 0xffff) + (w >> 16);
  172         return w ^ 0xffff;
  173 }
  174 
  175 /*
  176  * checksum_ip_header is applied to an IP direct from the
  177  * network interface, before any byteswapping.
  178  * Note that this means that the addition is
  179  * performed with the bytes of each halfword
  180  * reversed, but this is OK due to the magic
  181  * nature of ones complement arithmetic!
  182  * A checksum in network order is returned.
  183  */
  184 static int checksum_ip_header(struct ip_header *ip)
  185 {
  186         half_word_t *wp;
  187         int all, i, ip_header_halfwords;
  188 
  189         all = - ip->header_checksum;     /* don't count checksum field */
  190         wp = (half_word_t *) ip;
  191         ip_header_halfwords = ip->header_words * 2;
  192         for (i = 0; i < ip_header_halfwords; i++)
  193                 all += wp[i];
  194         return (finish_checksum(all));
  195 }
  196 
  197 static boolean_t ip_checksum_ok(struct ip_header *ip)
  198 {
  199         int net_check;
  200 
  201         net_check = ip->header_checksum;
  202         return ((net_check == 0) || (checksum_ip_header(ip) == net_check));
  203 }
  204 
  205 static void set_ip_checksum(struct ip_header *ip)
  206 {
  207         ip->header_checksum = checksum_ip_header(ip);
  208 }
  209 
  210 static void byteswap_ip_header(struct ip_header *ip_hdr)
  211 {
  212         ip_hdr->length          = netswap_2_bytes(ip_hdr->length);
  213         ip_hdr->id              = netswap_2_bytes(ip_hdr->id);
  214         ip_hdr->fragment_stuff  = netswap_2_bytes(ip_hdr->fragment_stuff);
  215         ip_hdr->header_checksum = netswap_2_bytes(ip_hdr->header_checksum);
  216 }
  217 
  218 /*
  219  * Use this after checking the IP header checksum.
  220  * A checksum in network order is returned.
  221  */
  222 static int checksum_udp(struct udp_header       *udp,
  223                         struct udp_pseudo_header pseudo_hdr)
  224 {
  225         half_word_t *wp;
  226         byte_t *bp;
  227         int all, i, udp_bytes, udp_halfwords;
  228 
  229         all = - udp->checksum;     /* don't count checksum field */
  230         wp = (half_word_t *) &pseudo_hdr;
  231         for (i = 0; i < sizeof(struct udp_pseudo_header) / sizeof(half_word_t); i++)
  232                 all += wp[i];
  233         udp_bytes = netswap_2_bytes(udp->length);
  234         udp_halfwords = udp_bytes / 2;
  235         wp = (half_word_t *) udp;
  236         for (i = 0; i <= udp_halfwords - 1; i++)
  237                 all += wp[i];
  238         if ((udp_bytes % 2) == 1) {
  239                 bp = (byte_t *) wp;
  240                 all += bp[udp_bytes - 1];
  241         }
  242         return (finish_checksum (all));
  243 }
  244 
  245 static boolean_t udp_checksum_ok(struct udp_header              *udp,
  246                                  struct udp_pseudo_header       pseudo_hdr)
  247 {
  248         int net_check;
  249 
  250         net_check = udp->checksum;
  251         return ((net_check == 0) || 
  252                 (checksum_udp(udp, pseudo_hdr) == net_check));
  253 }
  254 
  255 static void set_udp_checksum(struct udp_header          *udp,
  256                              struct udp_pseudo_header   ph)
  257 {
  258         udp->checksum = checksum_udp(udp, ph);
  259 }
  260 
  261 /*****************************************/
  262 /*       Simple-minded ARP Server        */
  263 /*****************************************/
  264 
  265 static boolean_t eq_ip(struct ip_address a, struct ip_address b)
  266 {
  267         int i;
  268         
  269         for (i = 0; i < IP_ADDRESS_LENGTH; i++)
  270                 if (a.array[i] != b.array[i])
  271                         return FALSE;
  272         return TRUE;
  273 }
  274 
  275 static foocount = 10;
  276 
  277 void dump_arp_packet(struct ttd_ether_header * e, arp_packet_t p)
  278 {
  279         printf("\neh.dest %x:%x:%x:%x:%x:%x, ",
  280                e->dest.array[0],
  281                e->dest.array[1],
  282                e->dest.array[2],
  283                e->dest.array[3],
  284                e->dest.array[4],
  285                e->dest.array[5]);
  286         printf("eh.source %x:%x:%x:%x:%x:%x, ",
  287                e->source.array[0],
  288                e->source.array[1],
  289                e->source.array[2],
  290                e->source.array[3],
  291                e->source.array[4],
  292                e->source.array[4]);
  293         printf("eh.protocol = %x, ", e->protocol);
  294         printf("arp.hat %x, ", p->hardware_addr_type);
  295         printf("arp.pat %x, ", p->protocol_addr_type);
  296         printf("arp.hal %x, ", p->hardware_addr_length);
  297         printf("arp.pal %x, ", p->protocol_addr_length);
  298         printf("arp.aop %x, ", p->arp_opcode);
  299         printf("arp.sha %x:%x:%x:%x:%x:%x",
  300                p->ip.source_hardware_addr.array[0],
  301                p->ip.source_hardware_addr.array[1],
  302                p->ip.source_hardware_addr.array[2],
  303                p->ip.source_hardware_addr.array[3],
  304                p->ip.source_hardware_addr.array[4],
  305                p->ip.source_hardware_addr.array[5]);
  306         printf("arp.spa %d.%d.%d.%d, ",
  307                p->ip.source_protocol_addr.array[0],
  308                p->ip.source_protocol_addr.array[1],
  309                p->ip.source_protocol_addr.array[2],
  310                p->ip.source_protocol_addr.array[3]);
  311         printf("arp.dha %x:%x:%x:%x:%x:%x",
  312                p->ip.dest_hardware_addr.array[0],
  313                p->ip.dest_hardware_addr.array[1],
  314                p->ip.dest_hardware_addr.array[2],
  315                p->ip.dest_hardware_addr.array[3],
  316                p->ip.dest_hardware_addr.array[4],
  317                p->ip.dest_hardware_addr.array[5]);
  318         printf("arp.dpa %d.%d.%d.%d, ",
  319                p->ip.dest_protocol_addr.array[0],
  320                p->ip.dest_protocol_addr.array[1],
  321                p->ip.dest_protocol_addr.array[2],
  322                p->ip.dest_protocol_addr.array[3]);
  323 
  324         if (!foocount--)
  325                 while(1);
  326 }
  327 
  328 static void handle_arp_packet(int unit,
  329                               struct ttd_ether_header * ehp,
  330                               struct packet_header * pkt)
  331         
  332 {
  333         arp_packet_t p;
  334         arpether_packet_t rpkt;
  335 
  336         /*
  337          * Set to point at the separate arp part of the received message
  338          */
  339         p = skip_pkt_header(pkt);
  340         
  341         /* ARP is simple; req is turned into reply and xmitted inline. */
  342 
  343         if (p->hardware_addr_type != ARPTYPE_ETHER) {   /* != 0x0100 */
  344                 return;
  345         }
  346         if (p->protocol_addr_type != ETHERTYPE_IP) {    /* != 0x0008 */
  347                 return;
  348         }
  349         if (p->hardware_addr_length != ETHER_ADDRESS_LENGTH) {  /* != 0x06 */
  350                 return;
  351         }
  352         if (p->protocol_addr_length != IP_ADDRESS_LENGTH) {     /* != 0x04 */
  353                 return;
  354         }
  355         if (p->arp_opcode != ARPOP_REQUEST) {   /* != 0x0100 */
  356                 return;
  357         }
  358         if (!eq_ip(p->ip.dest_protocol_addr, my_ip_addr)) {
  359                 return;
  360         }
  361 
  362         /*
  363          * Fix up the reply message:
  364          */
  365 
  366         rpkt = (arpether_packet_t)ttd_reply_msg;
  367 
  368         bzero(rpkt, MIN_PACKET);
  369 
  370         rpkt->eh.dest = ehp->source;
  371         rpkt->eh.source = ttd_host_ether_id;
  372         rpkt->eh.protocol = ETHERTYPE_ARP;
  373 
  374         rpkt->arp.hardware_addr_type = ARPTYPE_ETHER;
  375         rpkt->arp.protocol_addr_type = ETHERTYPE_IP;
  376         rpkt->arp.hardware_addr_length = ETHER_ADDRESS_LENGTH;
  377         rpkt->arp.protocol_addr_length = IP_ADDRESS_LENGTH;
  378 
  379         rpkt->arp.ip.dest_protocol_addr = p->ip.source_protocol_addr;
  380         rpkt->arp.ip.dest_hardware_addr = p->ip.source_hardware_addr;
  381         rpkt->arp.ip.source_protocol_addr = my_ip_addr;
  382         rpkt->arp.ip.source_hardware_addr = ttd_host_ether_id;
  383         rpkt->arp.arp_opcode = ARPOP_REPLY;
  384 
  385         /*
  386          * Send a reply....
  387          */
  388 
  389         ttd_send_packet(ttd_device_unit, rpkt, MIN_PACKET);
  390 }
  391 
  392 /*****************************************/
  393 /*        Bootp Packet Handling          */
  394 /*****************************************/
  395 
  396 /*
  397  * Generate a ``random'' transaction id number.  It builds it out of the
  398  * hardware ethernet id.  It's not unique -- XXX
  399  */
  400 static uint32 build_xid(void)
  401 {
  402         natural_t ret;
  403 
  404         ret = ((ttd_host_ether_id.array[0] <<12) ||
  405                (ttd_host_ether_id.array[5] <<8) ||
  406                (ttd_host_ether_id.array[1] <<4) ||
  407                ttd_host_ether_id.array[4]);
  408 
  409         ret ^= ((ttd_host_ether_id.array[2] <<12) ||
  410                 (ttd_host_ether_id.array[3] <<4));
  411 
  412         return ret;
  413 }
  414 
  415 void dump_ipudpbootp(char * s, sndbootp_t ptr)
  416 {
  417         recbootp_t      p = (recbootp_t)&ptr->ui;
  418         bootp_t         bp = (bootp_t)p->bpbuf;
  419 
  420         printf("%s",s);
  421 
  422         if (bp->bp_op == BOOTREQUEST) {
  423                 printf(", bootrequest is ok\n");
  424                 return;
  425         }else{
  426                 printf("\n");
  427         }
  428 
  429         printf("UDP Header: source %d (%d)\n",p->ui.udp_h.source_port,
  430                ((natural_t)&p->ui.udp_h.dest_port-(natural_t)&p->ui.udp_h.source_port));
  431         printf("dest %d, (%d)\n",p->ui.udp_h.dest_port, 
  432                ((natural_t)&p->ui.udp_h.length - (natural_t)&p->ui.udp_h.dest_port));
  433         printf("length %d, (%d)\n",p->ui.udp_h.length, 
  434                ((natural_t)&p->ui.udp_h.checksum - (natural_t)&p->ui.udp_h.length));
  435         printf("checksum %d, (%d)\n",p->ui.udp_h.checksum,
  436                ((natural_t)&bp->bp_op - (natural_t)&p->ui.udp_h.checksum));
  437         printf("BOOTP Contents: bp_op %d, (%d)\n", bp->bp_op,
  438                ((natural_t)&bp->bp_htype - (natural_t)&bp->bp_op));
  439         printf("bp_htype %d, (%d)\n", bp->bp_htype,
  440                ((natural_t)&bp->bp_hlen - (natural_t)&bp->bp_htype));
  441         printf("bp_hlen %d, (%d)\n", bp->bp_hlen,
  442                ((natural_t)&bp->bp_hops - (natural_t)&bp->bp_hlen));
  443         printf("bp_hops %d, (%d)\n", bp->bp_hops,
  444                ((natural_t)&bp->bp_xid - (natural_t)&bp->bp_hops));
  445         printf("bp_xid %d, (%d)\n", bp->bp_xid,
  446                ((natural_t)&bp->bp_secs - (natural_t)&bp->bp_xid));
  447         printf("bp_secs %d, (%d)\n", bp->bp_secs,
  448                ((natural_t)&bp->bp_unused - (natural_t)&bp->bp_secs));
  449         printf("bp_unused %d, (%d)\n", bp->bp_unused,
  450                ((natural_t)&bp->bp_ciaddr - (natural_t)&bp->bp_unused));
  451         printf("bp_ciaddr %d.%d.%d.%d, (%d)\n",
  452                bp->bp_ciaddr.array[0],
  453                bp->bp_ciaddr.array[1],
  454                bp->bp_ciaddr.array[2],
  455                bp->bp_ciaddr.array[3],
  456                ((natural_t)&bp->bp_yiaddr - (natural_t)&bp->bp_ciaddr));
  457         printf("bp_yiaddr %d.%d.%d.%d, (%d)\n",
  458                bp->bp_yiaddr.array[0],
  459                bp->bp_yiaddr.array[1],
  460                bp->bp_yiaddr.array[2],
  461                bp->bp_yiaddr.array[3],
  462                ((natural_t)&bp->bp_siaddr - (natural_t)&bp->bp_yiaddr));
  463         printf("bp_siaddr %d.%d.%d.%d, (%d)\n",
  464                bp->bp_siaddr.array[0],
  465                bp->bp_siaddr.array[1],
  466                bp->bp_siaddr.array[2],
  467                bp->bp_siaddr.array[3],
  468                ((natural_t)&bp->bp_giaddr - (natural_t)&bp->bp_siaddr));
  469         printf("bp_giaddr %d.%d.%d.%d, (%d)\n",
  470                bp->bp_giaddr.array[0],
  471                bp->bp_giaddr.array[1],
  472                bp->bp_giaddr.array[2],
  473                bp->bp_giaddr.array[3],
  474                ((natural_t)&bp->bp_chaddr - (natural_t)&bp->bp_giaddr));
  475         printf("bp_chaddr %x:%x:%x:%x:%x:%x, (%d)\n",
  476                bp->bp_chaddr[0],
  477                bp->bp_chaddr[1],
  478                bp->bp_chaddr[2],
  479                bp->bp_chaddr[3],
  480                bp->bp_chaddr[4],
  481                bp->bp_chaddr[5],
  482                ((natural_t)&bp->bp_sname - (natural_t)&bp->bp_chaddr));
  483 
  484         while(1);
  485 }
  486 
  487 /*
  488  * Builds an ethernet packet that contains a valid bootp
  489  * request.  This consists of a valid ethernet header, valid
  490  * ip header, udp header, and bootp request record.
  491  */
  492 static void build_bootp_packet(sndbootp_t buf, int size)
  493 {
  494         bootp_t         bp;
  495         uint32          tmp;
  496 
  497         bp = (bootp_t)buf->bpbuf;
  498 
  499         /*
  500          * Clean out the buffer.
  501          */
  502         bzero(buf,size);
  503 
  504         /*
  505          * Set up ethernet header:
  506          */
  507         buf->eh.dest = broadcast;
  508         buf->eh.source = ttd_host_ether_id;
  509         buf->eh.protocol = ETHERTYPE_IP;
  510 
  511         /*
  512          * Set up ip header:
  513          */
  514         buf->ui.ip_h.header_checksum = 0;
  515         buf->ui.ip_h.id = 1;
  516         buf->ui.ip_h.fragment_stuff = 0;
  517 
  518         /* Don't know about these two!! XXX */
  519         buf->ui.ip_h.type_of_service = 0;
  520         buf->ui.ip_h.version = 4;
  521 
  522         buf->ui.ip_h.header_words = OUT_IP_HEADER_WORDS;
  523         buf->ui.ip_h.time_to_live = 0xff;
  524 
  525         buf->ui.ip_h.protocol = PROTOCOL_UDP;
  526         buf->ui.ip_h.source =   ip_undefined;
  527         buf->ui.ip_h.dest =     ip_broadcast;
  528         buf->ui.ip_h.length =   sizeof(struct recbootp);
  529 
  530         /* Fix up for network byte order. */
  531         byteswap_ip_header(&buf->ui.ip_h);
  532         set_ip_checksum(&buf->ui.ip_h);
  533 
  534         /*
  535          * Set up the udp header:
  536          */
  537         buf->ui.udp_h.source_port =     netswap_2_bytes(UDP_BOOTPC);
  538         buf->ui.udp_h.dest_port =       netswap_2_bytes(UDP_BOOTPS);
  539         buf->ui.udp_h.length =          netswap_2_bytes(sizeof(struct udp_header)+
  540                                                         sizeof(struct bootp));
  541         buf->ui.udp_h.checksum =        0;
  542 
  543         /*
  544          * Set up bootp info:
  545          */
  546         bp->bp_op =             BOOTREQUEST;
  547         bp->bp_htype =          ETHER_HTYPE;
  548         bp->bp_hlen =           ETHER_ADDRESS_LENGTH;
  549         tmp = netswap_4_bytes(build_xid());
  550         bp->bp_xid[0] =         tmp & 0xf;
  551         bp->bp_xid[1] =         (tmp & 0xf0) >> 8;
  552         bp->bp_xid[2] =         (tmp & 0xf00) >> 8;
  553         bp->bp_xid[3] =         (tmp & 0xf000) >> 8;
  554         bp->bp_chaddr[0] =      ttd_host_ether_id.array[0];
  555         bp->bp_chaddr[1] =      ttd_host_ether_id.array[1];
  556         bp->bp_chaddr[2] =      ttd_host_ether_id.array[2];
  557         bp->bp_chaddr[3] =      ttd_host_ether_id.array[3];
  558         bp->bp_chaddr[4] =      ttd_host_ether_id.array[4];
  559         bp->bp_chaddr[5] =      ttd_host_ether_id.array[5];
  560 }
  561 
  562 /*
  563  * Check an incoming message for a valid bootp reply.
  564  */
  565 static boolean_t check_bootp_reply(void)
  566 {
  567         recbootp_t                      rbootp;
  568         struct ttd_ether_header *       ehp;
  569         bootp_t                         bp;
  570         uint32                          tmp;
  571 
  572         /*
  573          * Set up the pointers to point at the separate ether and ip data
  574          * in the netkmsg.
  575          */
  576         ehp = (struct ttd_ether_header *)
  577                 &((net_rcv_msg_t)&((ipc_kmsg_t)ttd_request_msg)->ikm_header)->header[0];
  578         rbootp = (recbootp_t)((natural_t)(&((net_rcv_msg_t)
  579                                       &((ipc_kmsg_t)ttd_request_msg)->ikm_header)->packet[0])
  580                               +(natural_t)(sizeof(struct packet_header)));
  581 
  582         bp = (bootp_t)rbootp->bpbuf;
  583 
  584 #if     DEBUG_ETHER_PACKETS
  585         printf("cBr.1 %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x ",
  586                ehp->dest.array[0],
  587                ehp->dest.array[1],
  588                ehp->dest.array[2],
  589                ehp->dest.array[3],
  590                ehp->dest.array[4],
  591                ehp->dest.array[5],
  592                ehp->source.array[0],
  593                ehp->source.array[1],
  594                ehp->source.array[2],
  595                ehp->source.array[3],
  596                ehp->source.array[4],
  597                ehp->source.array[5]);
  598 #endif  /* DEBUG_ETHER_PACKETS */
  599 
  600         /*
  601          * Check the ethernet header for my destination address and
  602          * the Ethernet IP protocol.
  603          */
  604         if (!ETHER_ADDRESS_EQ(&ehp->dest, &ttd_host_ether_id))
  605                 return FALSE;
  606         if (ehp->protocol != ETHERTYPE_IP)
  607                 return FALSE;
  608         /*
  609          * Got a packet for my machine.  Check the IP for datagram packet.
  610          */
  611 
  612         if (!ip_checksum_ok(&rbootp->ui.ip_h))
  613                 return FALSE;
  614         byteswap_ip_header(&rbootp->ui.ip_h);
  615 
  616         if (rbootp->ui.ip_h.protocol != PROTOCOL_UDP)
  617                 return FALSE;
  618 
  619         /*
  620          * It's a UDP datagram (maybe :-))
  621          * Check for bootp udp ports, udp length, xid, etc...
  622          */
  623 
  624         if (rbootp->ui.udp_h.source_port != netswap_2_bytes(UDP_BOOTPS))
  625                 return FALSE;
  626         if (rbootp->ui.udp_h.dest_port != netswap_2_bytes(UDP_BOOTPC))
  627                 return FALSE;
  628         if (netswap_2_bytes(rbootp->ui.udp_h.length) < 
  629             (sizeof(struct udp_header) + sizeof(struct bootp)))
  630                 return FALSE;
  631 
  632         if (bp->bp_op != BOOTREPLY)
  633                 return FALSE;
  634         tmp = netswap_4_bytes(build_xid());
  635         if ((bp->bp_xid[0] != tmp & 0xf) &&
  636             (bp->bp_xid[1] != (tmp & 0xf0) >> 8) &&
  637             (bp->bp_xid[2] != (tmp & 0xf00) >> 8) &&
  638             (bp->bp_xid[3] != (tmp & 0xf000) >> 8))
  639                 return FALSE;
  640 
  641         if (bp->bp_htype != ETHER_HTYPE)
  642                 return FALSE;
  643         if (bp->bp_hlen != ETHER_ADDRESS_LENGTH)
  644                 return FALSE;
  645 
  646         my_ip_addr = bp->bp_yiaddr;
  647         return TRUE;
  648 }
  649 
  650 /*
  651  * Try to get my host ip via the bootp protocol.  This routine
  652  * sends a bootp packet and then waits for a valid bootp return
  653  * call.  The routine returns true upon success, false if no bootp
  654  * server responds within the timeout period.
  655  */
  656 static boolean_t do_bootp(void)
  657 {
  658         natural_t max_tries;
  659         natural_t backoff;
  660         natural_t recvd;
  661 
  662         /*
  663          * Only send BOOTPMAX_TRIES bootp queries before giving up.
  664          */
  665         for(max_tries = BOOTPMAX_TRIES, backoff = 4;
  666             max_tries;
  667             max_tries--, backoff = ((backoff < 60) ? (backoff*2) : 60)) {
  668 
  669                 build_bootp_packet((sndbootp_t)ttd_request_msg, BOOTPMSG_SIZE);
  670 
  671                 ttd_send_packet(ttd_device_unit, ttd_request_msg, BOOTPMSG_SIZE);
  672 
  673                 for(recvd = 0;
  674                     recvd < backoff;
  675                     recvd++) {
  676                         /*
  677                          * Clean up receive buffer and get a message
  678                          */
  679                         bzero(ttd_request_msg, BOOTPMSG_SIZE);
  680                         ttd_get_packet(ttd_device_unit);
  681                         
  682                         if (check_bootp_reply())
  683                                 return TRUE;
  684                 }
  685         }
  686         return FALSE;
  687 }
  688 
  689 boolean_t ttd_ip_bootp(void)
  690 {
  691         boolean_t ret;
  692 
  693         /*
  694          * Turn on ttd so that ether drivers work with the polling routines.
  695          */
  696 
  697         printf("Looking for bootp server...");
  698         kttd_active = MAX_KTTD_ACTIVE;
  699         ret = do_bootp();
  700         kttd_active = MIN_KTTD_ACTIVE;
  701 
  702         if(ret) {
  703                 printf("found bootp server, ip address = %d.%d.%d.%d\n",
  704                     my_ip_addr.array[0],
  705                     my_ip_addr.array[1],
  706                     my_ip_addr.array[2],  
  707                     my_ip_addr.array[3]);
  708         }else{
  709                 kttd_enabled = FALSE;
  710                 printf("\ncouldn't find a bootp server.\n");
  711         }
  712 
  713         return ret;
  714 }
  715 
  716 /***************************************/
  717 /*         IP Packet Handling          */
  718 /***************************************/
  719 
  720 static void pseudo_header(struct ip_header *ip,
  721                           struct udp_pseudo_header *pseudo_hdr)
  722 {
  723         pseudo_hdr->source    = ip->source;
  724         pseudo_hdr->dest      = ip->dest;
  725         pseudo_hdr->zero      = 0;
  726         pseudo_hdr->protocol  = ip->protocol;
  727         /*  pseudo_hdr->udpLength = <set later>;  --  must be in network order */
  728 }
  729 
  730 void dump_ether_header(char * mess, struct ttd_ether_header * ehp)
  731 {
  732         printf("%s dhost: %x:%x:%x:%x:%x:%x",
  733                mess,
  734                ehp->dest.array[0],
  735                ehp->dest.array[1],
  736                ehp->dest.array[2],
  737                ehp->dest.array[3],
  738                ehp->dest.array[4],
  739                ehp->dest.array[5]);
  740 
  741         printf("  shost: %x:%x:%x:%x:%x:%x",
  742                ehp->source.array[0],
  743                ehp->source.array[1],
  744                ehp->source.array[2],
  745                ehp->source.array[3],
  746                ehp->source.array[4],
  747                ehp->source.array[5]);
  748 
  749         printf("  type = %x",ehp->protocol);
  750 }
  751 
  752 /*
  753  * New routines for kttd:
  754  */
  755 
  756 /*
  757  * poll_request:
  758  *
  759  * Get a packet from the ethernet via polling.
  760  *
  761  */
  762 static void kttd_poll_request(void)
  763 {
  764         ttd_get_packet(ttd_device_unit);
  765 }
  766 
  767 static boolean_t valid_udp_packet(struct udp_packet     *udp,
  768                                   struct udp_pseudo_header udp_phdr,
  769                                   natural_t             udp_length,
  770                                   vm_offset_t           *ttd_pkt)
  771 {
  772 
  773         udp_phdr.udp_length = udp->hdr.length;  /* in network order */
  774 
  775         if (!udp_checksum_ok(&udp->hdr, udp_phdr))
  776                 return FALSE;
  777 
  778         if (udp->hdr.dest_port != TTD_PORT)             /* in network order */
  779                 return FALSE;
  780 
  781         if (netswap_2_bytes(udp->hdr.length) > udp_length) {
  782                 if (kttd_debug)
  783                         printf("INVALID UDP Header Length!!!\n");
  784                 return FALSE;
  785         }
  786 
  787         *ttd_pkt = (vm_offset_t)&udp->data[0];
  788 
  789         return TRUE;
  790 }
  791 
  792 static boolean_t valid_ip_packet(ip_packet_t    *ip,
  793                                  natural_t      ip_len,
  794                                  struct udp_pseudo_header *udp_phdr)
  795 {
  796         /*
  797          * Make sure checksum adds up!
  798          */
  799         if (!ip_checksum_ok (&ip->hdr))
  800                 return FALSE;
  801 
  802         pseudo_header(&ip->hdr, udp_phdr); /* pseudo-header for UDP */
  803 
  804         byteswap_ip_header(&ip->hdr); /* Can't touch header fields before this! */
  805 
  806         /*
  807          * IP's length is bad, throw packet away...
  808          */
  809         if ((ip->hdr.length > ip_len) || (ip_len > MAX_ETHER_DATA)) {
  810                 byteswap_ip_header(&ip->hdr);
  811                 if (kttd_debug)
  812                         printf("INVALID IP LENGTH!!! 0x%x:0x%x\n",
  813                                ip->hdr.length, ip_len);
  814                 return FALSE;
  815         }
  816         
  817         /*
  818          * TTD packets are type UDP, discard others...
  819          */
  820         if (ip->hdr.protocol != PROTOCOL_UDP) {
  821                 byteswap_ip_header(&ip->hdr);
  822                 return FALSE;
  823         }
  824 
  825         return TRUE;
  826 
  827 }
  828 
  829 static boolean_t valid_ether_packet(struct ttd_ether_header     *ehp,
  830                                     struct packet_header        *pkt,
  831                                     boolean_t handle_arp)
  832 {
  833         /*
  834          * ignore broadcast replies
  835          */
  836         if (ETHER_ADDRESS_EQ (&ehp->source, &broadcast)) {
  837                 return FALSE;
  838         }
  839 
  840         /*
  841          * If arp packet then check for broadcast request.
  842          */
  843         if ((ehp->protocol == ETHERTYPE_ARP) && handle_arp) {
  844                 handle_arp_packet(ttd_device_unit, ehp, pkt);
  845                 return FALSE;
  846         }
  847 
  848         if (ehp->protocol != ETHERTYPE_IP) {
  849                 return FALSE;
  850         }
  851 
  852         /*
  853          * IP packet, means this might be a ttd packet.
  854          *
  855          * We aren't looking for broadcast ip dest though....
  856          */
  857 
  858         if (ETHER_ADDRESS_EQ(&ehp->dest, &broadcast))
  859                 return FALSE;
  860 
  861         /*
  862          * Passes the ethernet header check...
  863          */
  864         return TRUE;
  865 }
  866 
  867 /*
  868  * kttd_valid_request(request, handle_arp):
  869  *
  870  *  Given a pointer to an ipc_kmsg, determine whether it is a valid kttd
  871  * request packet.
  872  *
  873  *  Arp packets are handled here handle_arp is TRUE.
  874  *
  875  *  If the packet is a valid KTTD packet, the procedure sets the ttd_data
  876  * paramter to point to the start of the TTD message and returns TRUE.  If
  877  * it is not a valid ttd packet it returns FALSE.
  878  *
  879  */
  880 boolean_t kttd_valid_request(ipc_kmsg_t request,
  881                              boolean_t  handle_arp,
  882                              vm_offset_t *ttd_data,
  883                              natural_t *request_length)
  884 {
  885         struct ttd_ether_header *ehp;
  886         struct packet_header    *pkt;
  887 
  888         ip_packet_t             *ip;
  889         natural_t               length;
  890         natural_t               ip_hsize;
  891 
  892         struct udp_packet       *udp_pkt;
  893         struct udp_pseudo_header udp_phdr;
  894 #if     1
  895         int i;
  896         u_char *ptr;
  897 #endif  1
  898 
  899         vm_offset_t     ttd_pkt;
  900 
  901         /*
  902          * Check Ether packet:
  903          */
  904         ehp = (struct ttd_ether_header *)
  905                 &((net_rcv_msg_t)
  906                   &((ipc_kmsg_t)request)->ikm_header)->header[0];
  907         pkt = (struct packet_header *)
  908                 &((net_rcv_msg_t)
  909                   &((ipc_kmsg_t)request)->ikm_header)->packet[0];
  910 
  911         if (!valid_ether_packet(ehp, pkt, handle_arp))
  912                 return FALSE;
  913 
  914         /*
  915          * Check IP Packet:
  916          */
  917         length = pkt->length;
  918         ip = (ip_packet_t *)skip_pkt_header(pkt);
  919         
  920         if (!valid_ip_packet(ip, length, &udp_phdr))
  921                 return FALSE;
  922 
  923         /*
  924          * Check UDP Packet:
  925          */
  926 
  927         length -= sizeof(struct ip_header);
  928         ip_hsize = ip->hdr.header_words * 4;
  929         udp_pkt = (udp_packet_t) &ip->data[ip_hsize / 2];
  930 
  931         if (!valid_udp_packet(udp_pkt, udp_phdr, length, ttd_data)) {
  932                 byteswap_ip_header(&ip->hdr);   
  933                 return FALSE;
  934         }
  935 
  936         /*
  937          * Passed all of the tests, it's a TTD packet.
  938          */
  939 
  940         /* ttd_data was set by valid_udp_packet */
  941 
  942         *request_length = (length - sizeof(struct udp_header) -
  943                            sizeof(struct packet_header));
  944 
  945 #if     DEBUG_ETHER_PACKETS
  946         dump_ether_header("Ether header: ", ehp);
  947         printf("\nPacket contents: ");
  948         ptr = (u_char *)ip;
  949         for (i = 0; i < 100; i++) {
  950                 printf("%x:",(u_char) *ptr);
  951                 ptr++;
  952         }
  953         printf("\n");
  954 #endif  /* DEBUG_ETHER_PACKETS */
  955 
  956         return TRUE;
  957 }
  958 
  959 /*
  960  * kttd_get_request:
  961  *
  962  *  This routines waits for a ttd request packet to arrive on the
  963  * ethernet.  It does a polling input for a packet, and then checks
  964  * the packet for a valid ttd request.  It loops until it receives
  965  * a valid ttd request.
  966  *
  967  * Note:  this routine only called from the synchronous ttd code,
  968  *        so arp requests are handled.
  969  *
  970  * We assume that the ttd driver support has been checked and is
  971  * present.
  972  */
  973 boolean_t kttd_get_request(vm_offset_t  *ttd_request,
  974                            natural_t *request_length)
  975 {
  976 #if     VERBOSE
  977         if (kttd_debug)
  978                 printf("kttd_get_request entered.\n");
  979 #endif  /* VERBOSE */
  980 
  981         /*
  982          * clean it out, just to be safe.
  983          */
  984         bzero(ttd_request_msg, MAX_TTD_MSG_SIZE);
  985 
  986         for(;;) {
  987                 kttd_poll_request();
  988 
  989 #if     VERBOSE         
  990                 if (kttd_debug)
  991                         printf("-");
  992 #endif  /* VERBOSE */
  993 
  994                 if (kttd_valid_request((ipc_kmsg_t)ttd_request_msg,
  995                                        TRUE,
  996                                        (vm_offset_t *)ttd_request,
  997                                        request_length))
  998                         break;
  999         }
 1000 
 1001         return TRUE;
 1002 }
 1003 
 1004 /*
 1005  * Index into the reply.
 1006  */
 1007 ttd_reply_t skip_net_headers(char * ptr)
 1008 {
 1009         /*
 1010          * Skip ether header.
 1011          */
 1012         ptr += sizeof (struct ttd_ether_header);
 1013         
 1014         /*
 1015          * Skip IP header.
 1016          *
 1017          * This is dangerous!!!  IP header length is assumed to
 1018          * be constant for this to work.  Make sure when we
 1019          * build the reply packet that we are setting the header
 1020          * words field correctly.
 1021          */
 1022         ptr += OUT_IP_HEADER_BYTES;
 1023 
 1024         /*
 1025          * Skip UDP header.
 1026          */
 1027         ptr += sizeof (struct udp_header);
 1028 
 1029         return (ttd_reply_t)ptr;
 1030 }
 1031 
 1032 /*
 1033  * Wrap the old build and finish routines into one build
 1034  * routine.
 1035  */
 1036 void complete_and_send_ttd_reply(natural_t kttd_reply_length)
 1037 {
 1038         struct ttd_ether_header *req_ether_header;
 1039         struct packet_header    *req_ether_pkt;
 1040         struct ttd_ether_header *rpy_ether_header;
 1041 
 1042         struct ip_header        *req_ip_header;
 1043 
 1044         struct udp_header       *req_udp_header;
 1045 
 1046         struct udp_header       *rpy_udp_header;
 1047         struct ip_header        *rpy_ip_header;
 1048 
 1049         natural_t               tmp_length;
 1050         struct udp_pseudo_header uph;
 1051 
 1052         ipc_kmsg_t              tmp_kmsg;
 1053 
 1054         tmp_kmsg = (ipc_kmsg_t)((kttd_current_kmsg == (ipc_kmsg_t)NULL) ?
 1055                                 ttd_request_msg :
 1056                                 kttd_current_kmsg);
 1057 
 1058         /*
 1059          * Build the ethernet part of the reply.
 1060          */
 1061 
 1062         req_ether_header = (struct ttd_ether_header *)
 1063                 &((net_rcv_msg_t)&(tmp_kmsg)->ikm_header)->header[0];
 1064         req_ether_pkt = (struct packet_header *)
 1065                 &((net_rcv_msg_t)&(tmp_kmsg)->ikm_header)->packet[0];
 1066 
 1067 
 1068         /*
 1069          * The ttd_reply_msg is just the ether/ip/udp/ttd, it doesn't
 1070          * have the kmsg header junk that the ttd_request_msg has.
 1071          */
 1072         rpy_ether_header = (struct ttd_ether_header *)ttd_reply_msg;
 1073 
 1074         rpy_ether_header->source        = req_ether_header->dest;
 1075         rpy_ether_header->dest          = req_ether_header->source;
 1076         rpy_ether_header->protocol      = req_ether_header->protocol;
 1077 
 1078         /*
 1079          * Partially build the IP header.
 1080          *
 1081          * Note:  Everything at this point is NOT in network order.
 1082          *        The finish_ttd_build procedure must insert the
 1083          *        correct length, byteswap the reply header and 
 1084          *        generate the checksum and insert it.
 1085          */
 1086 
 1087         req_ip_header = (struct ip_header *)skip_pkt_header(req_ether_pkt);
 1088         rpy_ip_header = (struct ip_header *)((natural_t)rpy_ether_header +
 1089                                              sizeof (struct ttd_ether_header));
 1090 
 1091         rpy_ip_header->header_words     = OUT_IP_HEADER_WORDS;
 1092         rpy_ip_header->version          = req_ip_header->version;
 1093         rpy_ip_header->type_of_service  = req_ip_header->type_of_service;
 1094 /*      rpy_ip_header->length           = <set later>; */
 1095         rpy_ip_header->fragment_stuff   = 0;
 1096         rpy_ip_header->id               = 0;
 1097 /*      rpy_ip_header->header_checksum  = <set later>; */
 1098         rpy_ip_header->protocol         = req_ip_header->protocol;
 1099         rpy_ip_header->time_to_live     = 255;
 1100         rpy_ip_header->source           = req_ip_header->dest;
 1101         rpy_ip_header->dest             = req_ip_header->source;
 1102 
 1103         /*
 1104          * Partially build the UDP header.
 1105          *
 1106          * Note:  Same problem here as the IP layer.  We will still need
 1107          *        to set the length in the finish_ttd_build section along
 1108          *        with the checksum (if not using zero).
 1109          */
 1110 
 1111         req_udp_header = (struct udp_header *)((natural_t)req_ip_header +
 1112                                                ((natural_t)req_ip_header->header_words * 4));
 1113         rpy_udp_header = (struct udp_header *)((natural_t)rpy_ip_header +
 1114                                                OUT_IP_HEADER_BYTES);
 1115 
 1116         rpy_udp_header->source_port     = req_udp_header->dest_port;
 1117         rpy_udp_header->dest_port       = req_udp_header->source_port;
 1118 /*      rpy_udp_header->length          = <set later>; */
 1119 /*      rpy_udp_header->checksum        = <set later>; */
 1120 
 1121         /*
 1122          * Sanity check.  Make sure that the calculated ttd_reply_msg
 1123          * is the same one we just came up with!!!
 1124          */
 1125 
 1126         if (kttd_debug && ((natural_t) skip_net_headers(ttd_reply_msg) !=
 1127                            ((natural_t)rpy_udp_header + (sizeof(struct udp_header))))) {
 1128                 printf("TTDComm.c: Woah!!! ttd_reply_msg is skewed!!\n");
 1129         }
 1130             
 1131 #if     VERBOSE
 1132         if (kttd_debug)
 1133                 printf("build_partial: ttd_reply should start at: 0x%x, ttd_kmsg 0x%x\n",
 1134                        (natural_t)rpy_udp_header + sizeof(struct udp_header),
 1135                        ttd_reply_msg);
 1136 #endif  /* VERBOSE */
 1137 
 1138         /*
 1139          * Fix up UDP length and checksum:
 1140          */
 1141 
 1142         tmp_length = kttd_reply_length + sizeof(struct udp_header);
 1143         rpy_udp_header->length = netswap_2_bytes(tmp_length);
 1144 
 1145         uph.source = rpy_ip_header->source;
 1146         uph.dest = rpy_ip_header->dest;
 1147         uph.zero = 0;
 1148         uph.protocol = rpy_ip_header->protocol;
 1149         uph.udp_length = rpy_udp_header->length;
 1150 
 1151         set_udp_checksum(rpy_udp_header, uph);
 1152 
 1153         /*
 1154          * Fix up IP length and checksum:
 1155          */
 1156 
 1157         tmp_length += OUT_IP_HEADER_BYTES;
 1158         rpy_ip_header->length = tmp_length;
 1159         byteswap_ip_header(rpy_ip_header);
 1160         set_ip_checksum(rpy_ip_header);
 1161 
 1162 #if     DEBUG_ETHER_PACKETS
 1163         if (kttd_debug) {
 1164                 int i;
 1165                 u_char * ptr;
 1166 
 1167                 ptr = (u_char *)ttd_reply_msg;
 1168 
 1169                 for (i = 0; i< 100; i++)
 1170                         printf("%x: ", *ptr++);
 1171 
 1172                 printf("\nlength = %d",tmp_length);
 1173 
 1174         }
 1175 #endif  /* DEBUG_ETHER_PACKETS */
 1176 
 1177         /*
 1178          * We've built it, let's send it off!!!
 1179          */
 1180         ttd_send_packet(ttd_device_unit, ttd_reply_msg,
 1181                         ((MIN_PACKET > tmp_length + sizeof(struct ttd_ether_header)) ?
 1182                           MIN_PACKET : tmp_length + sizeof(struct ttd_ether_header)));
 1183 }

Cache object: fe8b29c6d0bfdae04c3ba0c2fe59c423


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.