Started adding structure to allow for separate protocol tests, with
[people/xl0/gpxe.git] / src / util / prototester.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <time.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9 #include <net/if.h>
10 #include <net/ethernet.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <getopt.h>
14
15 typedef int irq_action_t;
16
17 struct nic {
18         struct nic_operations   *nic_op;
19         unsigned char           *node_addr;
20         unsigned char           *packet;
21         unsigned int            packetlen;
22         void                    *priv_data;     /* driver private data */
23 };
24
25 struct nic_operations {
26         int ( *connect ) ( struct nic * );
27         int ( *poll ) ( struct nic *, int retrieve );
28         void ( *transmit ) ( struct nic *, const char *,
29                              unsigned int, unsigned int, const char * );
30         void ( *irq ) ( struct nic *, irq_action_t );
31 };
32
33 /*****************************************************************************
34  *
35  * Net device layer
36  *
37  */
38
39 #include "../proto/uip/uip_arp.h"
40
41 static unsigned char node_addr[ETH_ALEN];
42 static unsigned char packet[ETH_FRAME_LEN];
43 struct nic static_nic = {
44         .node_addr = node_addr,
45         .packet = packet,
46 };
47
48 /* Must be a macro because priv_data[] is of variable size */
49 #define alloc_netdevice( priv_size ) ( {        \
50         static char priv_data[priv_size];       \
51         static_nic.priv_data = priv_data;       \
52         &static_nic; } )
53
54 static int register_netdevice ( struct nic *nic ) {
55         struct uip_eth_addr hwaddr;
56
57         memcpy ( &hwaddr, nic->node_addr, sizeof ( hwaddr ) );
58         uip_setethaddr ( hwaddr );
59         return 0;
60 }
61
62 static inline void unregister_netdevice ( struct nic *nic ) {
63         /* Do nothing */
64 }
65
66 static inline void free_netdevice ( struct nic *nic ) {
67         /* Do nothing */
68 }
69
70 static int netdev_poll ( int retrieve, void **data, size_t *len ) {
71         int rc = static_nic.nic_op->poll ( &static_nic, retrieve );
72         *data = static_nic.packet;
73         *len = static_nic.packetlen;
74         return rc;
75 }
76
77 static void netdev_transmit ( const void *data, size_t len ) {
78         uint16_t type = ntohs ( *( ( uint16_t * ) ( data + 12 ) ) );
79         static_nic.nic_op->transmit ( &static_nic, data, type,
80                                       len - ETH_HLEN,
81                                       data + ETH_HLEN );
82 }
83
84 /*****************************************************************************
85  *
86  * Hijack device interface
87  *
88  * This requires a hijack daemon to be running
89  *
90  */
91
92 struct hijack {
93         int fd;
94 };
95
96 struct hijack_device {
97         char *name;
98         void *priv;
99 };
100
101 static inline void hijack_set_drvdata ( struct hijack_device *hijack_dev,
102                                         void *data ) {
103         hijack_dev->priv = data;
104 }
105
106 static inline void * hijack_get_drvdata ( struct hijack_device *hijack_dev ) {
107         return hijack_dev->priv;
108 }
109
110 static int hijack_poll ( struct nic *nic, int retrieve ) {
111         struct hijack *hijack = nic->priv_data;
112         fd_set fdset;
113         struct timeval tv;
114         int ready;
115         ssize_t len;
116
117         /* Poll for data */
118         FD_ZERO ( &fdset );
119         FD_SET ( hijack->fd, &fdset );
120         tv.tv_sec = 0;
121         tv.tv_usec = 500; /* 500us to avoid hogging CPU */
122         ready = select ( ( hijack->fd + 1 ), &fdset, NULL, NULL, &tv );
123         if ( ready < 0 ) {
124                 fprintf ( stderr, "select() failed: %s\n",
125                           strerror ( errno ) );
126                 return 0;
127         }
128         if ( ready == 0 )
129                 return 0;
130
131         /* Return if we're not retrieving data yet */
132         if ( ! retrieve )
133                 return 1;
134
135         /* Fetch data */
136         len = read ( hijack->fd, nic->packet, ETH_FRAME_LEN );
137         if ( len < 0 ) {
138                 fprintf ( stderr, "read() failed: %s\n",
139                           strerror ( errno ) );
140                 return 0;
141         }
142         nic->packetlen = len;
143
144         return 1;
145 }
146
147 static void hijack_transmit ( struct nic *nic, const char *dest,
148                               unsigned int type, unsigned int size,
149                               const char *packet ) {
150         struct hijack *hijack = nic->priv_data;
151         unsigned int nstype = htons ( type );
152         unsigned int total_size = ETH_HLEN + size;
153         char txbuf[ total_size ];
154
155         /* Build packet header */
156         memcpy ( txbuf, dest, ETH_ALEN );
157         memcpy ( txbuf + ETH_ALEN, nic->node_addr, ETH_ALEN );
158         memcpy ( txbuf + 2 * ETH_ALEN, &nstype, 2 );
159         memcpy ( txbuf + ETH_HLEN, packet, size );
160
161         /* Transmit data */
162         if ( write ( hijack->fd, txbuf, total_size ) != total_size ) {
163                 fprintf ( stderr, "write() failed: %s\n",
164                           strerror ( errno ) );
165         }
166 }
167
168 static int hijack_connect ( struct nic *nic ) {
169         return 1;
170 }
171
172 static void hijack_irq ( struct nic *nic, irq_action_t action ) {
173         /* Do nothing */
174 }
175
176 static struct nic_operations hijack_operations = {
177         .connect        = hijack_connect,
178         .transmit       = hijack_transmit,
179         .poll           = hijack_poll,
180         .irq            = hijack_irq,
181 };
182
183 int hijack_probe ( struct hijack_device *hijack_dev ) {
184         struct nic *nic;
185         struct hijack *hijack;
186         struct sockaddr_un sun;
187         int i;
188
189         nic = alloc_netdevice ( sizeof ( *hijack ) );
190         if ( ! nic ) {
191                 fprintf ( stderr, "alloc_netdevice() failed\n" );
192                 goto err_alloc;
193         }
194         hijack = nic->priv_data;
195         memset ( hijack, 0, sizeof ( *hijack ) );
196
197         /* Create socket */
198         hijack->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
199         if ( hijack->fd < 0 ) {
200                 fprintf ( stderr, "socket() failed: %s\n",
201                           strerror ( errno ) );
202                 goto err;
203         }
204
205         /* Connect to hijack daemon */
206         sun.sun_family = AF_UNIX;
207         snprintf ( sun.sun_path, sizeof ( sun.sun_path ), "/var/run/hijack-%s",
208                    hijack_dev->name );
209         if ( connect ( hijack->fd, ( struct sockaddr * ) &sun,
210                        sizeof ( sun ) ) < 0 ) {
211                 fprintf ( stderr, "could not connect to %s: %s\n",
212                           sun.sun_path, strerror ( errno ) );
213                 goto err;
214         }
215
216         /* Generate MAC address */
217         srand ( time ( NULL ) );
218         for ( i = 0 ; i < ETH_ALEN ; i++ ) {
219                 nic->node_addr[i] = ( rand() & 0xff );
220         }
221         nic->node_addr[0] &= 0xfe; /* clear multicast bit */
222         nic->node_addr[0] |= 0x02; /* set "locally-assigned" bit */
223
224         nic->nic_op = &hijack_operations;
225         if ( register_netdevice ( nic ) < 0 )
226                 goto err;
227
228         hijack_set_drvdata ( hijack_dev, nic );
229         return 1;
230
231  err:
232         if ( hijack->fd >= 0 )
233                 close ( hijack->fd );
234         free_netdevice ( nic );
235  err_alloc:
236         return 0;
237 }
238
239 static void hijack_disable ( struct hijack_device *hijack_dev ) {
240         struct nic *nic = hijack_get_drvdata ( hijack_dev );
241         struct hijack *hijack = nic->priv_data;
242         
243         unregister_netdevice ( nic );
244         close ( hijack->fd );
245 }
246
247 /*****************************************************************************
248  *
249  * uIP wrapper layer
250  *
251  */
252
253 #include "../proto/uip/uip.h"
254 #include "../proto/uip/uip_arp.h"
255
256 void UIP_APPCALL ( void ) {
257         printf ( "appcall\n" );
258 }
259
260 void udp_appcall ( void ) {
261 }
262
263 static void init_tcpip ( void ) {
264         uip_init();
265         uip_arp_init();
266 }
267
268 static void uip_transmit ( void ) {
269         uip_arp_out();
270         netdev_transmit ( uip_buf, uip_len );
271         uip_len = 0;
272 }
273
274 static int tcp_connect ( struct sockaddr_in *sin ) {
275         u16_t ipaddr[2];
276
277         * ( ( uint32_t * ) ipaddr ) = sin->sin_addr.s_addr;
278         return uip_connect ( ipaddr, sin->sin_port ) ? 1 : 0;
279 }
280
281 static void run_tcpip ( void ) {
282         void *data;
283         size_t len;
284         uint16_t type;
285         int i;
286         
287         if ( netdev_poll ( 1, &data, &len ) ) {
288                 /* We have data */
289                 memcpy ( uip_buf, data, len );
290                 uip_len = len;
291                 type = ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) );
292                 if ( type == ETHERTYPE_ARP ) {
293                         uip_arp_arpin();
294                 } else {
295                         uip_arp_ipin();
296                         uip_input();
297                 }
298                 if ( uip_len > 0 )
299                         uip_transmit();
300         } else {
301                 for ( i = 0 ; i < UIP_CONNS ; i++ ) {
302                         uip_periodic ( i );
303                         if ( uip_len > 0 )
304                                 uip_transmit();
305                 }
306         }
307 }
308
309 /*****************************************************************************
310  *
311  * HTTP protocol tester
312  *
313  */
314
315 struct http_request {
316         struct sockaddr_in sin;
317         const char *filename;
318         void ( *callback ) ( struct http_request *http );
319         int complete;
320 };
321
322 static int http_get ( struct http_request *http ) {
323         return tcp_connect ( &http->sin );
324 }
325
326 static void test_http_callback ( struct http_request *http ) {
327         
328 }
329
330 static int test_http ( int argc, char **argv ) {
331         struct http_request http;
332
333         memset ( &http, 0, sizeof ( http ) );
334         http.filename = "/";
335         http.callback = test_http_callback;
336         inet_aton ( "192.168.0.1", &http.sin.sin_addr );
337         http.sin.sin_port = htons ( 80 );
338
339         http_get ( &http );
340         
341         while ( ! http.complete ) {
342                 run_tcpip ();
343         }
344
345         return 0;
346 }
347
348 /*****************************************************************************
349  *
350  * Protocol tester
351  *
352  */
353
354 struct protocol_test {
355         const char *name;
356         int ( *exec ) ( int argc, char **argv );
357 };
358
359 static struct protocol_test tests[] = {
360         { "http", test_http },
361 };
362
363 #define NUM_TESTS ( sizeof ( tests ) / sizeof ( tests[0] ) )
364
365 static void list_tests ( void ) {
366         int i;
367
368         for ( i = 0 ; i < NUM_TESTS ; i++ ) {
369                 printf ( "%s\n", tests[i].name );
370         }
371 }
372
373 static struct protocol_test * get_test_from_name ( const char *name ) {
374         int i;
375
376         for ( i = 0 ; i < NUM_TESTS ; i++ ) {
377                 if ( strcmp ( name, tests[i].name ) == 0 )
378                         return &tests[i];
379         }
380
381         return NULL;
382 }
383
384 /*****************************************************************************
385  *
386  * Parse command-line options
387  *
388  */
389
390 struct tester_options {
391         char interface[IF_NAMESIZE];
392 };
393
394 static void usage ( char **argv ) {
395         fprintf ( stderr,
396                   "Usage: %s [global options] <test> [test-specific options]\n"
397                   "\n"
398                   "Global options:\n"
399                   "  -h|--help              Print this help message\n"
400                   "  -i|--interface intf    Use specified network interface\n"
401                   "  -l|--list              List available tests\n"
402                   "\n"
403                   "Use \"%s <test> -h\" to view test-specific options\n",
404                   argv[0], argv[0] );
405 }
406
407 static int parse_options ( int argc, char **argv,
408                            struct tester_options *options ) {
409         static struct option long_options[] = {
410                 { "interface", 1, NULL, 'i' },
411                 { "list", 0, NULL, 'l' },
412                 { "help", 0, NULL, 'h' },
413                 { },
414         };
415         int c;
416
417         /* Set default options */
418         memset ( options, 0, sizeof ( *options ) );
419         strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
420
421         /* Parse command-line options */
422         while ( 1 ) {
423                 int option_index = 0;
424                 
425                 c = getopt_long ( argc, argv, "+i:hl", long_options,
426                                   &option_index );
427                 if ( c < 0 )
428                         break;
429
430                 switch ( c ) {
431                 case 'i':
432                         strncpy ( options->interface, optarg,
433                                   sizeof ( options->interface ) );
434                         break;
435                 case 'l':
436                         list_tests ();
437                         return -1;
438                 case 'h':
439                         usage ( argv );
440                         return -1;
441                 case '?':
442                         /* Unrecognised option */
443                         return -1;
444                 default:
445                         fprintf ( stderr, "Unrecognised option '-%c'\n", c );
446                         return -1;
447                 }
448         }
449
450         /* Check there is a test specified */
451         if ( optind == argc ) {
452                 usage ( argv );
453                 return -1;
454         }
455         
456         return optind;
457 }
458
459 /*****************************************************************************
460  *
461  * Main program
462  *
463  */
464
465 int main ( int argc, char **argv ) {
466         struct tester_options options;
467         struct protocol_test *test;
468         struct hijack_device hijack_dev;
469
470         /* Parse command-line options */
471         if ( parse_options ( argc, argv, &options ) < 0 )
472                 exit ( 1 );
473
474         /* Identify test */
475         test = get_test_from_name ( argv[optind] );
476         if ( ! test ) {
477                 fprintf ( stderr, "Unrecognised test \"%s\"\n", argv[optind] );
478                 return -1;
479         }
480         optind++;
481
482         /* Initialise the protocol stack */
483         init_tcpip();
484
485         /* Open the hijack device */
486         hijack_dev.name = options.interface;
487         if ( ! hijack_probe ( &hijack_dev ) )
488                 exit ( 1 );
489
490         /* Run the test */
491         if ( test->exec ( argc, argv ) < 0 )
492                 exit ( 1 );
493
494         /* Close the hijack device */
495         hijack_disable ( &hijack_dev );
496
497         return 0;
498 }