c49a1bca35286f30ffc1f01c3633522d9b8e6584
[people/mcb30/gpxe.git] / src / core / gdbudp.c
1 /*
2  * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <byteswap.h>
22 #include <bios.h>
23 #include <gpxe/iobuf.h>
24 #include <gpxe/in.h>
25 #include <gpxe/if_arp.h>
26 #include <gpxe/if_ether.h>
27 #include <gpxe/ip.h>
28 #include <gpxe/udp.h>
29 #include <gpxe/netdevice.h>
30 #include <gpxe/gdbstub.h>
31 #include <gpxe/gdbudp.h>
32
33 /** @file
34  *
35  * GDB over UDP transport
36  *
37  */
38
39 enum {
40         DEFAULT_PORT = 43770, /* UDP listen port */
41 };
42
43 struct gdb_transport udp_gdb_transport __gdb_transport;
44
45 static struct net_device *netdev;
46 static uint8_t dest_eth[ETH_ALEN];
47 static struct sockaddr_in dest_addr;
48 static struct sockaddr_in source_addr;
49
50 static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) {
51         /* The device may have been closed between breakpoints */
52         assert ( netdev );
53         netdev_open ( netdev );
54
55         /* Strictly speaking, we may need to close the device when leaving the interrupt handler */
56 }
57
58 static size_t gdbudp_recv ( char *buf, size_t len ) {
59         struct io_buffer *iob;
60         struct ethhdr *ethhdr;
61         struct arphdr *arphdr;
62         struct iphdr *iphdr;
63         struct udp_header *udphdr;
64         size_t payload_len;
65
66         gdbudp_ensure_netdev_open ( netdev );
67
68         for ( ; ; ) {
69                 netdev_poll ( netdev );
70                 while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) {
71                         /* Ethernet header */
72                         if ( iob_len ( iob ) < sizeof ( *ethhdr ) ) {
73                                 goto bad_packet;
74                         }
75                         ethhdr = iob->data;
76                         iob_pull ( iob, sizeof ( *ethhdr ) );
77
78                         /* Handle ARP requests so the client can find our MAC */
79                         if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) {
80                                 arphdr = iob->data;
81                                 if ( iob_len ( iob ) < sizeof ( *arphdr ) + 2 * ( ETH_ALEN + sizeof ( struct in_addr ) ) ||
82                                                 arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
83                                                 arphdr->ar_pro != htons ( ETH_P_IP ) ||
84                                                 arphdr->ar_hln != ETH_ALEN ||
85                                                 arphdr->ar_pln != sizeof ( struct in_addr ) ||
86                                                 arphdr->ar_op != htons ( ARPOP_REQUEST ) ||
87                                                 * ( uint32_t * ) arp_target_pa ( arphdr ) != source_addr.sin_addr.s_addr ) {
88                                         goto bad_packet;
89                                 }
90
91                                 /* Generate an ARP reply */
92                                 arphdr->ar_op = htons ( ARPOP_REPLY );
93                                 memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) );
94                                 memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN );
95                                 memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, ETH_ALEN );
96
97                                 /* Fix up ethernet header */
98                                 ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
99                                 memcpy ( ethhdr->h_dest, ethhdr->h_source, ETH_ALEN );
100                                 memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
101
102                                 netdev_tx ( netdev, iob );
103                                 continue; /* no need to free iob */
104                         }
105
106                         if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) {
107                                 goto bad_packet;
108                         }
109
110                         /* IP header */
111                         if ( iob_len ( iob ) < sizeof ( *iphdr ) ) {
112                                 goto bad_packet;
113                         }
114                         iphdr = iob->data;
115                         iob_pull ( iob, sizeof ( *iphdr ) );
116                         if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) {
117                                 goto bad_packet;
118                         }
119
120                         /* UDP header */
121                         if ( iob_len ( iob ) < sizeof ( *udphdr ) ) {
122                                 goto bad_packet;
123                         }
124                         udphdr = iob->data;
125                         if ( udphdr->dest != source_addr.sin_port ) {
126                                 goto bad_packet;
127                         }
128
129                         /* Learn the remote connection details */
130                         memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN );
131                         dest_addr.sin_addr.s_addr = iphdr->src.s_addr;
132                         dest_addr.sin_port = udphdr->src;
133
134                         /* Payload */
135                         payload_len = ntohs ( udphdr->len );
136                         if ( payload_len < sizeof ( *udphdr ) || payload_len > iob_len ( iob ) ) {
137                                 goto bad_packet;
138                         }
139                         payload_len -= sizeof ( *udphdr );
140                         iob_pull ( iob, sizeof ( *udphdr ) );
141                         if ( payload_len > len ) {
142                                 goto bad_packet;
143                         }
144                         memcpy ( buf, iob->data, payload_len );
145
146                         free_iob ( iob );
147                         return payload_len;
148
149 bad_packet:
150                         free_iob ( iob );
151                 }
152                 cpu_nap();
153         }
154 }
155
156 static void gdbudp_send ( const char *buf, size_t len ) {
157         struct io_buffer *iob;
158         struct ethhdr *ethhdr;
159         struct iphdr *iphdr;
160         struct udp_header *udphdr;
161
162         /* Check that we are connected */
163         if ( dest_addr.sin_port == 0 ) {
164                 return;
165         }
166
167         gdbudp_ensure_netdev_open ( netdev );
168
169         iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len );
170         if ( !iob ) {
171                 return;
172         }
173
174         /* Payload */
175         iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) );
176         memcpy ( iob_put ( iob, len ), buf, len );
177
178         /* UDP header */
179         udphdr = iob_push ( iob, sizeof ( *udphdr ) );
180         udphdr->src = source_addr.sin_port;
181         udphdr->dest = dest_addr.sin_port;
182         udphdr->len = htons ( iob_len ( iob ) );
183         udphdr->chksum = 0; /* optional and we are not using it */
184
185         /* IP header */
186         iphdr = iob_push ( iob, sizeof ( *iphdr ) );
187         memset ( iphdr, 0, sizeof ( *iphdr ) );
188         iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
189         iphdr->service = IP_TOS;
190         iphdr->len = htons ( iob_len ( iob ) ); 
191         iphdr->ttl = IP_TTL;
192         iphdr->protocol = IP_UDP;
193         iphdr->dest.s_addr = dest_addr.sin_addr.s_addr;
194         iphdr->src.s_addr = source_addr.sin_addr.s_addr;
195         iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
196
197         /* Ethernet header */
198         ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
199         memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN );
200         memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN );
201         ethhdr->h_protocol = htons ( ETH_P_IP );
202
203         netdev_tx ( netdev, iob );
204 }
205
206 struct gdb_transport *gdbudp_configure ( const char *name, struct sockaddr_in *addr ) {
207         struct settings *settings;
208
209         /* Release old network device */
210         netdev_put ( netdev );
211
212         netdev = find_netdev ( name );
213         if ( !netdev ) {
214                 return NULL;
215         }
216
217         /* Hold network device */
218         netdev_get ( netdev );
219
220         /* Source UDP port */
221         source_addr.sin_port = ( addr && addr->sin_port ) ? addr->sin_port : htons ( DEFAULT_PORT );
222
223         /* Source IP address */
224         if ( addr && addr->sin_addr.s_addr ) {
225                 source_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
226         } else {
227                 settings = netdev_settings ( netdev );
228                 fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr );
229                 if ( source_addr.sin_addr.s_addr == 0 ) {
230                         netdev_put ( netdev );
231                         netdev = NULL;
232                         return NULL;
233                 }
234         }
235
236         return &udp_gdb_transport;
237 }
238
239 static int gdbudp_init ( int argc, char **argv ) {
240         if ( argc != 1 ) {
241                 printf ( "udp: missing <interface> argument\n" );
242                 return 1;
243         }
244
245         if ( !gdbudp_configure ( argv[0], NULL ) ) {
246                 printf ( "%s: device does not exist or has no IP address\n", argv[0] );
247                 return 1;
248         }
249         return 0;
250 }
251
252 struct gdb_transport udp_gdb_transport __gdb_transport = {
253         .name = "udp",
254         .init = gdbudp_init,
255         .send = gdbudp_send,
256         .recv = gdbudp_recv,
257 };