Skeleton version: contains device driver (connecting to network via
[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 <getopt.h>
13
14 typedef int irq_action_t;
15
16 struct nic {
17         struct nic_operations   *nic_op;
18         unsigned char           *node_addr;
19         unsigned char           *packet;
20         unsigned int            packetlen;
21         void                    *priv_data;     /* driver private data */
22 };
23
24 struct nic_operations {
25         int ( *connect ) ( struct nic * );
26         int ( *poll ) ( struct nic *, int retrieve );
27         void ( *transmit ) ( struct nic *, const char *,
28                              unsigned int, unsigned int, const char * );
29         void ( *irq ) ( struct nic *, irq_action_t );
30 };
31
32 /*****************************************************************************
33  *
34  * Hijack device interface
35  *
36  * This requires a hijack daemon to be running
37  *
38  */
39
40 struct hijack {
41         int fd;
42 };
43
44 struct hijack_device {
45         char *name;
46 };
47
48 static int hijack_poll ( struct nic *nic, int retrieve ) {
49         struct hijack *hijack = nic->priv_data;
50         fd_set fdset;
51         struct timeval tv;
52         int ready;
53         ssize_t len;
54
55         /* Poll for data */
56         FD_ZERO ( &fdset );
57         FD_SET ( hijack->fd, &fdset );
58         tv.tv_sec = 0;
59         tv.tv_usec = 500; /* 500us to avoid hogging CPU */
60         ready = select ( ( hijack->fd + 1 ), &fdset, NULL, NULL, &tv );
61         if ( ready < 0 ) {
62                 fprintf ( stderr, "select() failed: %s\n",
63                           strerror ( errno ) );
64                 return 0;
65         }
66         if ( ready == 0 )
67                 return 0;
68
69         /* Return if we're not retrieving data yet */
70         if ( ! retrieve )
71                 return 1;
72
73         /* Fetch data */
74         len = read ( hijack->fd, nic->packet, ETH_FRAME_LEN );
75         if ( len < 0 ) {
76                 fprintf ( stderr, "read() failed: %s\n",
77                           strerror ( errno ) );
78                 return 0;
79         }
80         nic->packetlen = len;
81
82         return 1;
83 }
84
85 static void hijack_transmit ( struct nic *nic, const char *dest,
86                               unsigned int type, unsigned int size,
87                               const char *packet ) {
88         struct hijack *hijack = nic->priv_data;
89         unsigned int nstype = htons ( type );
90         unsigned int total_size = ETH_HLEN + size;
91         char txbuf[ total_size ];
92
93         /* Build packet header */
94         memcpy ( txbuf, dest, ETH_ALEN );
95         memcpy ( txbuf + ETH_ALEN, nic->node_addr, ETH_ALEN );
96         memcpy ( txbuf + 2 * ETH_ALEN, &nstype, 2 );
97         memcpy ( txbuf + ETH_HLEN, packet, size );
98
99         /* Transmit data */
100         if ( write ( hijack->fd, txbuf, total_size ) != total_size ) {
101                 fprintf ( stderr, "write() failed: %s\n",
102                           strerror ( errno ) );
103         }
104 }
105
106 static int hijack_connect ( struct nic *nic ) {
107         return 1;
108 }
109
110 static void hijack_irq ( struct nic *nic, irq_action_t action ) {
111         /* Do nothing */
112 }
113
114 static struct nic_operations hijack_operations = {
115         .connect        = hijack_connect,
116         .transmit       = hijack_transmit,
117         .poll           = hijack_poll,
118         .irq            = hijack_irq,
119 };
120
121 static int hijack_probe ( struct nic *nic, struct hijack_device *hijack_dev ) {
122         static struct hijack hijack;
123         struct sockaddr_un sun;
124         int i;
125
126         memset ( &hijack, 0, sizeof ( hijack ) );
127
128         /* Create socket */
129         hijack.fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
130         if ( hijack.fd < 0 ) {
131                 fprintf ( stderr, "socket() failed: %s\n",
132                           strerror ( errno ) );
133                 goto err;
134         }
135
136         /* Connect to hijack daemon */
137         sun.sun_family = AF_UNIX;
138         snprintf ( sun.sun_path, sizeof ( sun.sun_path ), "/var/run/hijack-%s",
139                    hijack_dev->name );
140         if ( connect ( hijack.fd, ( struct sockaddr * ) &sun,
141                        sizeof ( sun ) ) < 0 ) {
142                 fprintf ( stderr, "could not connect to %s: %s\n",
143                           sun.sun_path, strerror ( errno ) );
144                 goto err;
145         }
146
147         /* Generate MAC address */
148         srand ( time ( NULL ) );
149         for ( i = 0 ; i < ETH_ALEN ; i++ ) {
150                 nic->node_addr[i] = ( rand() & 0xff );
151         }
152         nic->node_addr[0] &= 0xfe; /* clear multicast bit */
153         nic->node_addr[0] |= 0x02; /* set "locally-assigned" bit */
154
155         nic->priv_data = &hijack;
156         nic->nic_op = &hijack_operations;
157         return 1;
158
159  err:
160         if ( hijack.fd >= 0 )
161                 close ( hijack.fd );
162         return 0;
163 }
164
165 static void hijack_disable ( struct nic *nic,
166                              struct hijack_device *hijack_dev ) {
167         struct hijack *hijack = nic->priv_data;
168         
169         close ( hijack->fd );
170 }
171
172 /*****************************************************************************
173  *
174  * Parse command-line options
175  *
176  */
177
178 struct tester_options {
179         char interface[IF_NAMESIZE];
180 };
181
182 static void usage ( char **argv ) {
183         fprintf ( stderr,
184                   "Usage: %s [options]\n"
185                   "\n"
186                   "Options:\n"
187                   "  -h|--help              Print this help message\n"
188                   "  -i|--interface intf    Use specified network interface\n",
189                   argv[0] );
190 }
191
192 static int parse_options ( int argc, char **argv,
193                            struct tester_options *options ) {
194         static struct option long_options[] = {
195                 { "interface", 1, NULL, 'i' },
196                 { "help", 0, NULL, 'h' },
197                 { },
198         };
199         int c;
200
201         /* Set default options */
202         memset ( options, 0, sizeof ( *options ) );
203         strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
204
205         /* Parse command-line options */
206         while ( 1 ) {
207                 int option_index = 0;
208                 
209                 c = getopt_long ( argc, argv, "i:h", long_options,
210                                   &option_index );
211                 if ( c < 0 )
212                         break;
213
214                 switch ( c ) {
215                 case 'i':
216                         strncpy ( options->interface, optarg,
217                                   sizeof ( options->interface ) );
218                         break;
219                 case 'h':
220                         usage( argv );
221                         return -1;
222                 case '?':
223                         /* Unrecognised option */
224                         return -1;
225                 default:
226                         fprintf ( stderr, "Unrecognised option '-%c'\n", c );
227                         return -1;
228                 }
229         }
230
231         /* Check there's nothing left over on the command line */
232         if ( optind != argc ) {
233                 usage ( argv );
234                 return -1;
235         }
236
237         return 0;
238 }
239
240 /*****************************************************************************
241  *
242  * uIP wrapper layer
243  *
244  */
245
246 #include "../proto/uip/uip.h"
247 #include "../proto/uip/uip_arp.h"
248
249 static int done;
250
251 void UIP_APPCALL ( void ) {
252         printf ( "appcall\n" );
253 }
254
255 void udp_appcall ( void ) {
256 }
257
258 static void uip_transmit ( struct nic *nic ) {
259         uip_arp_out();
260         nic->nic_op->transmit ( nic, ( char * ) uip_buf,
261                                 ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) ),
262                                 uip_len - ETH_HLEN,
263                                 ( char * ) uip_buf + ETH_HLEN );
264         uip_len = 0;
265 }
266
267 static void run_stack ( struct nic *nic ) {
268         struct uip_eth_addr hwaddr;
269         u16_t ipaddr[2];
270         uint16_t type;
271         int i;
272
273         uip_init();
274         uip_arp_init();
275         memcpy ( &hwaddr, nic->node_addr, sizeof ( hwaddr ) );
276         uip_setethaddr ( hwaddr );
277
278         uip_ipaddr(ipaddr, 192,168,0,1);
279         uip_connect(ipaddr, HTONS(80));
280
281         done = 0;
282         while ( ! done ) {
283                 if ( nic->nic_op->poll ( nic, 1 ) ) {
284                         /* We have data */
285                         memcpy ( uip_buf, nic->packet, nic->packetlen );
286                         uip_len = nic->packetlen;
287                         type = ntohs ( *( ( uint16_t * ) ( uip_buf + 12 ) ) );
288                         if ( type == ETHERTYPE_ARP ) {
289                                 uip_arp_arpin();
290                         } else {
291                                 uip_arp_ipin();
292                                 uip_input();
293                         }
294                         if ( uip_len > 0 )
295                                 uip_transmit ( nic );
296                 } else {
297                         for ( i = 0 ; i < UIP_CONNS ; i++ ) {
298                                 uip_periodic ( i );
299                                 if ( uip_len > 0 )
300                                         uip_transmit ( nic );
301                         }
302                 }
303         }
304 }
305
306 /*****************************************************************************
307  *
308  * Main program
309  *
310  */
311
312 int main ( int argc, char **argv ) {
313         struct tester_options options;
314         struct hijack_device hijack_dev;
315         static unsigned char node_addr[ETH_ALEN];
316         static unsigned char packet[ETH_FRAME_LEN];
317         struct nic nic = {
318                 .node_addr = node_addr,
319                 .packet = packet,
320         };
321
322         /* Parse command-line options */
323         if ( parse_options ( argc, argv, &options ) < 0 )
324                 exit ( 1 );
325
326         /* Open the hijack device */
327         hijack_dev.name = options.interface;
328         if ( ! hijack_probe ( &nic, &hijack_dev ) )
329                 exit ( 1 );
330
331         /* Run the stack to completion */
332         run_stack ( &nic );
333
334         /* Close the hijack device */
335         hijack_disable ( &nic, &hijack_dev );
336
337         return 0;
338 }