iPXE
gdbudp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <byteswap.h>
00029 #include <ipxe/iobuf.h>
00030 #include <ipxe/in.h>
00031 #include <ipxe/if_arp.h>
00032 #include <ipxe/if_ether.h>
00033 #include <ipxe/ip.h>
00034 #include <ipxe/udp.h>
00035 #include <ipxe/netdevice.h>
00036 #include <ipxe/nap.h>
00037 #include <ipxe/gdbstub.h>
00038 #include <ipxe/gdbudp.h>
00039 
00040 /** @file
00041  *
00042  * GDB over UDP transport
00043  *
00044  */
00045 
00046 enum {
00047         DEFAULT_PORT = 43770, /* UDP listen port */
00048 };
00049 
00050 struct gdb_transport udp_gdb_transport __gdb_transport;
00051 
00052 static struct net_device *netdev;
00053 static uint8_t dest_eth[ETH_ALEN];
00054 static struct sockaddr_in dest_addr;
00055 static struct sockaddr_in source_addr;
00056 
00057 static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) {
00058         /* The device may have been closed between breakpoints */
00059         assert ( netdev );
00060         netdev_open ( netdev );
00061 
00062         /* Strictly speaking, we may need to close the device when leaving the interrupt handler */
00063 }
00064 
00065 static size_t gdbudp_recv ( char *buf, size_t len ) {
00066         struct io_buffer *iob;
00067         struct ethhdr *ethhdr;
00068         struct arphdr *arphdr;
00069         struct iphdr *iphdr;
00070         struct udp_header *udphdr;
00071         size_t payload_len;
00072 
00073         gdbudp_ensure_netdev_open ( netdev );
00074 
00075         for ( ; ; ) {
00076                 netdev_poll ( netdev );
00077                 while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) {
00078                         /* Ethernet header */
00079                         if ( iob_len ( iob ) < sizeof ( *ethhdr ) ) {
00080                                 goto bad_packet;
00081                         }
00082                         ethhdr = iob->data;
00083                         iob_pull ( iob, sizeof ( *ethhdr ) );
00084 
00085                         /* Handle ARP requests so the client can find our MAC */
00086                         if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) {
00087                                 arphdr = iob->data;
00088                                 if ( iob_len ( iob ) < sizeof ( *arphdr ) + 2 * ( ETH_ALEN + sizeof ( struct in_addr ) ) ||
00089                                                 arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
00090                                                 arphdr->ar_pro != htons ( ETH_P_IP ) ||
00091                                                 arphdr->ar_hln != ETH_ALEN ||
00092                                                 arphdr->ar_pln != sizeof ( struct in_addr ) ||
00093                                                 arphdr->ar_op != htons ( ARPOP_REQUEST ) ||
00094                                                 * ( uint32_t * ) arp_target_pa ( arphdr ) != source_addr.sin_addr.s_addr ) {
00095                                         goto bad_packet;
00096                                 }
00097 
00098                                 /* Generate an ARP reply */
00099                                 arphdr->ar_op = htons ( ARPOP_REPLY );
00100                                 memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) );
00101                                 memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN );
00102                                 memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, ETH_ALEN );
00103 
00104                                 /* Fix up ethernet header */
00105                                 ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
00106                                 memcpy ( ethhdr->h_dest, ethhdr->h_source, ETH_ALEN );
00107                                 memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
00108 
00109                                 netdev_tx ( netdev, iob );
00110                                 continue; /* no need to free iob */
00111                         }
00112 
00113                         if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) {
00114                                 goto bad_packet;
00115                         }
00116 
00117                         /* IP header */
00118                         if ( iob_len ( iob ) < sizeof ( *iphdr ) ) {
00119                                 goto bad_packet;
00120                         }
00121                         iphdr = iob->data;
00122                         iob_pull ( iob, sizeof ( *iphdr ) );
00123                         if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) {
00124                                 goto bad_packet;
00125                         }
00126 
00127                         /* UDP header */
00128                         if ( iob_len ( iob ) < sizeof ( *udphdr ) ) {
00129                                 goto bad_packet;
00130                         }
00131                         udphdr = iob->data;
00132                         if ( udphdr->dest != source_addr.sin_port ) {
00133                                 goto bad_packet;
00134                         }
00135 
00136                         /* Learn the remote connection details */
00137                         memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN );
00138                         dest_addr.sin_addr.s_addr = iphdr->src.s_addr;
00139                         dest_addr.sin_port = udphdr->src;
00140 
00141                         /* Payload */
00142                         payload_len = ntohs ( udphdr->len );
00143                         if ( payload_len < sizeof ( *udphdr ) || payload_len > iob_len ( iob ) ) {
00144                                 goto bad_packet;
00145                         }
00146                         payload_len -= sizeof ( *udphdr );
00147                         iob_pull ( iob, sizeof ( *udphdr ) );
00148                         if ( payload_len > len ) {
00149                                 goto bad_packet;
00150                         }
00151                         memcpy ( buf, iob->data, payload_len );
00152 
00153                         free_iob ( iob );
00154                         return payload_len;
00155 
00156 bad_packet:
00157                         free_iob ( iob );
00158                 }
00159                 cpu_nap();
00160         }
00161 }
00162 
00163 static void gdbudp_send ( const char *buf, size_t len ) {
00164         struct io_buffer *iob;
00165         struct ethhdr *ethhdr;
00166         struct iphdr *iphdr;
00167         struct udp_header *udphdr;
00168 
00169         /* Check that we are connected */
00170         if ( dest_addr.sin_port == 0 ) {
00171                 return;
00172         }
00173 
00174         gdbudp_ensure_netdev_open ( netdev );
00175 
00176         iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len );
00177         if ( !iob ) {
00178                 return;
00179         }
00180 
00181         /* Payload */
00182         iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) );
00183         memcpy ( iob_put ( iob, len ), buf, len );
00184 
00185         /* UDP header */
00186         udphdr = iob_push ( iob, sizeof ( *udphdr ) );
00187         udphdr->src = source_addr.sin_port;
00188         udphdr->dest = dest_addr.sin_port;
00189         udphdr->len = htons ( iob_len ( iob ) );
00190         udphdr->chksum = 0; /* optional and we are not using it */
00191 
00192         /* IP header */
00193         iphdr = iob_push ( iob, sizeof ( *iphdr ) );
00194         memset ( iphdr, 0, sizeof ( *iphdr ) );
00195         iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
00196         iphdr->service = IP_TOS;
00197         iphdr->len = htons ( iob_len ( iob ) ); 
00198         iphdr->ttl = IP_TTL;
00199         iphdr->protocol = IP_UDP;
00200         iphdr->dest.s_addr = dest_addr.sin_addr.s_addr;
00201         iphdr->src.s_addr = source_addr.sin_addr.s_addr;
00202         iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
00203 
00204         /* Ethernet header */
00205         ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
00206         memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN );
00207         memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
00208         ethhdr->h_protocol = htons ( ETH_P_IP );
00209 
00210         netdev_tx ( netdev, iob );
00211 }
00212 
00213 struct gdb_transport *gdbudp_configure ( const char *name, struct sockaddr_in *addr ) {
00214         struct settings *settings;
00215 
00216         /* Release old network device */
00217         netdev_put ( netdev );
00218 
00219         netdev = find_netdev ( name );
00220         if ( !netdev ) {
00221                 return NULL;
00222         }
00223 
00224         /* Hold network device */
00225         netdev_get ( netdev );
00226 
00227         /* Source UDP port */
00228         source_addr.sin_port = ( addr && addr->sin_port ) ? addr->sin_port : htons ( DEFAULT_PORT );
00229 
00230         /* Source IP address */
00231         if ( addr && addr->sin_addr.s_addr ) {
00232                 source_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
00233         } else {
00234                 settings = netdev_settings ( netdev );
00235                 fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr );
00236                 if ( source_addr.sin_addr.s_addr == 0 ) {
00237                         netdev_put ( netdev );
00238                         netdev = NULL;
00239                         return NULL;
00240                 }
00241         }
00242 
00243         return &udp_gdb_transport;
00244 }
00245 
00246 static int gdbudp_init ( int argc, char **argv ) {
00247         if ( argc != 1 ) {
00248                 printf ( "udp: missing <interface> argument\n" );
00249                 return 1;
00250         }
00251 
00252         if ( !gdbudp_configure ( argv[0], NULL ) ) {
00253                 printf ( "%s: device does not exist or has no IP address\n", argv[0] );
00254                 return 1;
00255         }
00256         return 0;
00257 }
00258 
00259 struct gdb_transport udp_gdb_transport __gdb_transport = {
00260         .name = "udp",
00261         .init = gdbudp_init,
00262         .send = gdbudp_send,
00263         .recv = gdbudp_recv,
00264 };