2 * @(#) jig to exercise a UML/FreeSWAN kernel with two interfaces
4 * Copyright (C) 2001 Michael Richardson <mcr@freeswan.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * RCSID $Id: uml_netjig.c,v 1.8 2002/01/21 01:08:41 mcr Exp $
18 * @(#) based upon uml_router from User-Mode-Linux tools package
23 * This file contains a program to excersize a FreeSWAN kernel that is
24 * built in a User-Mode-Linux form. It creates four sets of Unix
25 * domain sockets: two control sockets and two data sockets.
27 * These sockets make up the connection points for the "daemon" method
28 * of networking provided by UML.
30 * The first connection is intended to connect to "eth0" (the inside
31 * or "private" network) and the second one to "eth1" (the outside or
34 * Packets are fed into one network interface from a (pcap) capture file and
35 * are captured from the other interface into a pcap capture file.
37 * The program can take an argument which is a script/program to run
38 * with the appropriate UML arguments. This can be the UML kernel
39 * itself, a script that invokes it or something that just records
42 * The environment variables UML_{public,private}_{CTL,DATA} are set to
43 * the names of the respective sockets.
45 * If the --arp option is given, the program will respond to all ARP
46 * requests that it sees, providing a suitable response.
48 * Note that the program continues to operate as a switch and will
49 * accept multiple connections. All packets are logged and the
50 * outgoing packets are sent to wherever the destination MAC address
56 #include <sys/types.h>
64 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #include <net/ethernet.h>
68 #include <net/if_arp.h>
74 #include <sys/queue.h>
78 #define MAX(x,y) ((x) > (y) ? (x) : (y))
81 #include "netdissect.h"
83 struct netdissect_options gndo;
84 int tcpdump_print = 1;
88 TAILQ_ENTRY(connection) link;
91 struct sockaddr_un name;
92 unsigned char addr[ETH_ALEN];
95 enum request_type { REQ_NEW_CONTROL };
98 enum request_type type;
101 unsigned char addr[ETH_ALEN];
102 struct sockaddr_un name;
105 unsigned long cookie;
112 unsigned char dest[6];
113 unsigned char src[6];
116 unsigned char data[1500];
120 TAILQ_HEAD(,connection) connections;
122 unsigned char nh_defaultether[6];
123 struct in_addr nh_defaultgate;
126 pcap_dumper_t *nh_output;
129 char *ctl_socket_name;
131 char *data_socket_name;
135 struct nethub public, private;
137 static fd_set perm_fds;
138 static int max_fd = -1;
139 static char *progname;
141 void *xmalloc1(size_t size, char *file, int linenum)
145 space = malloc(size);
147 fprintf(stderr, "no memory allocating %d bytes at %s:%d\n",
148 size, file, linenum);
154 #define xmalloc(X) xmalloc1((X), __FILE__, __LINE__)
156 static void cleanup(struct nethub *nh)
158 if(unlink(nh->ctl_socket_name) < 0){
159 printf("Couldn't remove control socket '%s' : %s\n",
160 nh->ctl_socket_name, strerror(errno));
162 if(unlink(nh->data_socket_name) < 0){
163 printf("Couldn't remove data socket '%s' : %s\n",
164 nh->data_socket_name, strerror(errno));
168 static void sig_handler(int sig)
170 printf("Caught signal %d, cleaning up and exiting\n", sig);
173 signal(sig, SIG_DFL);
177 static void close_descriptor(int fd)
179 FD_CLR(fd, &perm_fds);
183 static void free_connection(struct nethub *nh,
184 struct connection *conn)
186 close_descriptor(conn->control);
187 FD_CLR(conn->control, &perm_fds);
189 TAILQ_REMOVE(&nh->connections, conn, link);
194 static void service_connection(struct nethub *nh,
195 struct connection *conn)
200 n = read(conn->control, &req, sizeof(req));
202 perror("Reading request");
203 free_connection(nh, conn);
207 printf("%s: disconnect from hw address %02x:%02x:%02x:%02x:%02x:%02x\n",
209 conn->addr[0], conn->addr[1], conn->addr[2], conn->addr[3],
210 conn->addr[4], conn->addr[5]);
211 free_connection(nh, conn);
215 case REQ_NEW_CONTROL:
216 memcpy(conn->addr, req.u.new_control.addr, sizeof(conn->addr));
217 conn->name = req.u.new_control.name;
220 printf("%s: new connection - hw address %02x:%02x:%02x:%02x:%02x:%02x\n",
222 conn->addr[0], conn->addr[1], conn->addr[2], conn->addr[3],
223 conn->addr[4], conn->addr[5]);
226 printf("Bad request type : %d\n", req.type);
227 free_connection(nh, conn);
231 static int match_addr(unsigned char *host, unsigned char *packet)
233 if(packet[0] & 1) return(1);
234 return((packet[0] == host[0]) && (packet[1] == host[1]) &&
235 (packet[2] == host[2]) && (packet[3] == host[3]) &&
236 (packet[4] == host[4]) && (packet[5] == host[5]));
239 static void forward_data(struct nethub *nh,
243 struct connection *c, *next;
245 for(c = nh->connections.tqh_first; c != NULL; c = next){
246 next = c->link.tqe_next;
248 match_addr(c->addr, p->header.dest)){
249 sendto(nh->data_fd, p, len,
250 0, (struct sockaddr *) &c->name, sizeof(c->name));
255 static void handle_data(struct nethub *nh)
257 struct pcap_pkthdr ph;
261 len = recvfrom(nh->data_fd, &p, sizeof(p), 0, NULL, 0);
263 if(errno != EAGAIN) perror("Reading data");
267 memset(&ph, 0, sizeof(ph));
271 if(nh->nh_outputFile) {
272 pcap_dump((u_char *)nh->nh_output, &ph, (u_char *)&p);
276 /* now dump it to tcpdump dissector if one was configured */
278 printf("%8s:", nh->nh_name);
279 ether_if_print((u_char *)&gndo, &ph, (u_char *)&p);
284 if(nh->nh_defaultgate.s_addr!=0 || nh->nh_allarp) {
285 if(p.header.proto == htons(ETHERTYPE_ARP)) {
288 ahdr = (struct arphdr *)&p.data;
289 if(ahdr->ar_hrd == htons(ARPHRD_ETHER) &&
290 ahdr->ar_pro == htons(ETHERTYPE_IP) &&
291 ahdr->ar_hln == ETH_ALEN &&
293 ahdr->ar_op == htons(ARPOP_REQUEST)) {
296 sip = (u_int32_t *)(p.data + /*sizeof(arphdr)*/8 + 1*ETH_ALEN);
297 tip = (u_int32_t *)(p.data + /*sizeof(arphdr)*/8 + 2*ETH_ALEN + 4);
299 if(nh->nh_allarp == 1 || *tip == nh->nh_defaultgate.s_addr) {
300 /* AHA! reply to ARP request */
302 /* we mutate this packet in place */
303 /* change this to a reply */
304 ahdr->ar_op = htons(ARPOP_REPLY);
306 /* swap ether fields */
307 memcpy(p.header.dest, p.header.src, ETH_ALEN);
309 memcpy(p.data + 8, nh->nh_defaultether, ETH_ALEN);
310 memcpy(p.header.src, nh->nh_defaultether, ETH_ALEN);
320 printf("%s: found ARP request, replying: \n", nh->nh_name);
323 struct pcap_pkthdr ph;
325 memset(&ph, 0, sizeof(ph));
330 printf("%8s:", nh->nh_name);
331 ether_if_print((u_char *)&gndo, &ph, (u_char *)&p);
339 forward_data(nh, &p, len);
343 static void new_connection(struct nethub *nh,
346 struct connection *conn;
348 conn = xmalloc(sizeof(struct connection));
351 close_descriptor(fd);
359 TAILQ_INSERT_TAIL(&nh->connections, conn, link);
362 void handle_connections(struct nethub *nh,
363 fd_set *fds, int max)
365 struct connection *c;
369 if(FD_ISSET(i, fds)){
371 for(c = nh->connections.tqh_first;
373 c = c->link.tqe_next)
376 service_connection(nh, c);
385 void accept_connection(struct nethub *nh)
387 struct sockaddr addr;
390 new = accept(nh->ctl_listen_fd, &addr, &len);
395 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
396 perror("fcntl - setting O_NONBLOCK");
400 if(new > max_fd) max_fd = new;
401 FD_SET(new, &perm_fds);
403 new_connection(nh, new);
407 int still_used(struct sockaddr_un *sun)
409 int test_fd, ret = 1;
411 if((test_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
416 if(connect(test_fd, (struct sockaddr *) sun, sizeof(*sun)) < 0){
417 if(errno == ECONNREFUSED){
418 if(unlink(sun->sun_path) < 0){
419 fprintf(stderr, "Failed to removed unused socket '%s': ",
425 else perror("connect");
432 int bind_socket(int fd, const char *name)
434 struct sockaddr_un sun;
436 sun.sun_family = AF_UNIX;
437 strcpy(sun.sun_path, name);
439 if(bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
440 if((errno == EADDRINUSE) && still_used(&sun)) return(EADDRINUSE);
441 else if(bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
450 void bind_sockets(struct nethub *nh)
452 int ctl_err, ctl_present = 0, ctl_used = 0;
453 int data_err, data_present = 0, data_used = 0;
454 int try_remove_ctl, try_remove_data;
456 ctl_err = bind_socket(nh->ctl_listen_fd, nh->ctl_socket_name);
457 if(ctl_err != 0) ctl_present = 1;
458 if(ctl_err == EADDRINUSE) ctl_used = 1;
460 data_err = bind_socket(nh->data_fd, nh->data_socket_name);
461 if(data_err != 0) data_present = 1;
462 if(data_err == EADDRINUSE) data_used = 1;
464 if(!ctl_err && !data_err) return;
466 unlink(nh->ctl_socket_name);
467 unlink(nh->data_socket_name);
469 try_remove_ctl = ctl_present;
470 try_remove_data = data_present;
471 if(ctl_present && ctl_used){
472 fprintf(stderr, "The control socket '%s' has another server "
473 "attached to it\n", nh->ctl_socket_name);
476 else if(ctl_present && !ctl_used)
477 fprintf(stderr, "The control socket '%s' exists, isn't used, but couldn't "
478 "be removed\n", nh->ctl_socket_name);
479 if(data_present && data_used){
480 fprintf(stderr, "The data socket '%s' has another server "
481 "attached to it\n", nh->data_socket_name);
484 else if(data_present && !data_used)
485 fprintf(stderr, "The data socket '%s' exists, isn't used, but couldn't "
486 "be removed\n", nh->data_socket_name);
487 if(try_remove_ctl || try_remove_data){
488 fprintf(stderr, "You can either\n");
489 if(try_remove_ctl && !try_remove_data)
490 fprintf(stderr, "\tremove '%s'\n", nh->ctl_socket_name);
491 else if(!try_remove_ctl && try_remove_data)
492 fprintf(stderr, "\tremove '%s'\n", nh->data_socket_name);
493 else fprintf(stderr, "\tremove '%s' and '%s'\n",
494 nh->ctl_socket_name, nh->data_socket_name);
495 fprintf(stderr, "\tor rerun with different, unused filenames for "
497 fprintf(stderr, "\t\t%s -unix <control> <data>\n", progname);
498 fprintf(stderr, "\t\tand run the UMLs with "
499 "'eth0=daemon,,unix,<control>,<data>\n");
503 fprintf(stderr, "You should rerun with different, unused filenames for "
505 fprintf(stderr, "\t%s -unix <control> <data>\n", progname);
506 fprintf(stderr, "\tand run the UMLs with "
507 "'eth0=daemon,,unix,<control>,<data>'\n");
512 static void Usage(void)
514 fprintf(stderr, "Usage : uml_netjig \n"
515 "Version $Revision: 1.8 $ \n\n"
516 "\t--exitonempty (-e) exit when no more packets to read\n"
517 "\t--playpublic (-p) <file> pcap(3) file to feed into public side\n"
518 "\t--recordpublic (-r) <file> pcap(3) file to write from public side\n"
519 "\t--playprivate (-P) <file> pcap(3) file to feed into private side\n"
520 "\t--recordprivate (-R) <file> pcap(3) file to write from private side\n"
521 "\t--unix (-u) <dir> directory to put sockets (default $TMPDIR)\n"
522 "\t--startup (-s) <script> script to run after sockets are setup.\n"
524 "\t--tcpdump (-t) dump packets with tcpdump-dissector\n"
526 "\t--tcpdump (-t) (not available - dissector not built in)\n"
529 "\t--arpreply (-a) respond to ARP requests\n"
531 "\t--arpreply (-a) (not available - arp replies disabled)\n"
533 "\t--help this message\n\n");
538 /* Like default_print() but data need not be aligned */
540 default_print_unaligned(struct netdissect_options *ipdo,
541 register const u_char *cp, register u_int length)
544 register int nshorts;
546 if (ipdo->ndo_Xflag) {
547 ascii_print(ipdo, cp, length);
550 nshorts = (u_int) length / sizeof(u_short);
552 while (--nshorts >= 0) {
554 (void)printf("\n\t\t\t");
556 (void)printf(" %02x%02x", s, *cp++);
560 (void)printf("\n\t\t\t");
561 (void)printf(" %02x", *cp);
566 * By default, print the packet out in hex.
569 default_print(struct netdissect_options *ndo,
570 register const u_char *bp, register u_int length)
572 default_print_unaligned(ndo, bp, length);
576 void init_nethub(struct nethub *nh, char *base, char *type)
583 memset(nh, 0, sizeof(*nh));
584 TAILQ_INIT(&nh->connections);
586 nh->nh_name = strdup(type);
588 /* setup ARP stuff */
591 nh->nh_defaultgate.s_addr = 0;
593 nh->nh_defaultether[0]=0x10;
594 nh->nh_defaultether[1]=0x00;
595 nh->nh_defaultether[2]=0x00;
596 nh->nh_defaultether[3]=type[0];
597 nh->nh_defaultether[4]=type[1];
598 nh->nh_defaultether[5]=type[2];
600 /* cons up the names, and stick them in the environment */
601 env = xmalloc(sizeof("UML_")+strlen(type)+sizeof("CTL=")+
602 strlen(base)+sizeof("/ctl")+1);
604 sprintf(env, "UML_%s_CTL=%s/ctl", type, base);
605 nh->ctl_socket_name = strchr(env, '=')+1;
608 env = xmalloc(sizeof("UML_")+strlen(type)+sizeof("DATA=")+
609 strlen(base)+sizeof("/data")+1);
611 sprintf(env, "UML_%s_DATA=%s/data", type, base);
612 nh->data_socket_name = strchr(env, '=')+1;
615 if((nh->ctl_listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
619 if(setsockopt(nh->ctl_listen_fd,
620 SOL_SOCKET, SO_REUSEADDR, (char *) &one,
622 perror("setsockopt");
626 if(fcntl(nh->ctl_listen_fd, F_SETFL, O_NONBLOCK) < 0){
627 perror("Setting O_NONBLOCK on connection fd");
631 if((nh->data_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
635 if(fcntl(nh->data_fd, F_SETFL, O_NONBLOCK) < 0){
636 perror("Setting O_NONBLOCK on data fd");
642 if(listen(nh->ctl_listen_fd, 15) < 0){
651 int main(int argc, char **argv)
655 char *publicbase, *privatebase;
656 char *basedir, *startup;
657 char *playprivatefile, *recordprivatefile;
658 char *playpublicfile, *recordpublicfile;
663 static struct option long_options[] =
665 {"help", no_argument, 0, 'h'},
666 {"arpreply", no_argument, 0, 'a'},
667 {"debug", no_argument, 0, 'd'},
668 {"exitonempty", no_argument, 0, 'e'},
669 {"tcpdump", no_argument, 0, 't'},
670 {"playpublic", required_argument, 0, 'p'},
671 {"playprivate",required_argument, 0, 'P'},
672 {"recordpublic", required_argument, 0, 'r'},
673 {"recordprivate", required_argument, 0, 'R'},
674 {"unix", required_argument, 0, 'u'},
675 {"startup", required_argument, 0, 's'},
685 playpublicfile = playprivatefile = NULL;
686 recordpublicfile= recordprivatefile = NULL;
689 if(strrchr(progname, '/')) {
690 progname=strrchr(progname, '/')+1;
693 while((opt = getopt_long(argc, argv, "adehp:P:r:R:s:tu:",
694 long_options, NULL)) != EOF) {
713 fprintf(stderr, "tcpdump dissector not available\n");
720 playpublicfile = optarg;
724 playprivatefile = optarg;
728 recordpublicfile= optarg;
732 recordprivatefile= optarg;
741 if(basedir == NULL) {
744 basedir=tempnam(NULL, "uml");
745 if(mkdir(basedir, 0700) == 0) {
751 fprintf(stderr, "failed to make tmpdir (last=%s)\n",
758 publicbase=xmalloc(strlen(basedir)+sizeof("/public")+1);
759 sprintf(publicbase, "%s/public", basedir);
760 if(mkdir(publicbase, 0700) != 0 && errno!=EEXIST) {
767 privatebase=xmalloc(strlen(basedir)+sizeof("/private")+1);
768 sprintf(privatebase, "%s/private", basedir);
769 if(mkdir(privatebase, 0700) != 0 && errno!=EEXIST) {
776 memset(&gndo, 0, sizeof(gndo));
777 gndo.ndo_default_print = default_print;
779 /* dump ethernet headers */
782 /* avoid DNS lookups */
786 init_nethub(&public, publicbase, "public");
787 init_nethub(&private,privatebase,"private");
789 public.nh_allarp = arpreply;
790 private.nh_allarp= arpreply;
792 if(recordpublicfile) {
795 printf("%s: will record to %s from public interface\n",
796 progname, recordpublicfile);
797 public.nh_outputFile = recordpublicfile;
799 pt = pcap_open_dead(DLT_EN10MB, 1536);
800 public.nh_output = pcap_dump_open(pt, recordpublicfile);
801 if(public.nh_output == NULL) {
802 fprintf(stderr, "pcap_dump_open failed to open %s\n",
808 if(recordprivatefile) {
811 printf("%s: will record to %s from private interface\n",
812 progname, recordprivatefile);
813 private.nh_outputFile = recordprivatefile;
815 pt = pcap_open_dead(DLT_EN10MB, 1536);
816 private.nh_output = pcap_dump_open(pt, recordprivatefile);
817 if(private.nh_output == NULL) {
818 fprintf(stderr, "pcap_dump_open failed to open %s\n",
825 printf("%s: will play %s to public interface\n",
826 progname, playpublicfile);
828 public.nh_inputFile = playpublicfile;
829 public.nh_input = pcap_open_offline(playpublicfile, errbuf);
830 if(public.nh_input == NULL) {
831 fprintf(stderr, "pcap_open_offline: %s\n", errbuf);
836 if(playprivatefile) {
837 printf("%s: will play %s to private interface\n",
838 progname, playprivatefile);
840 private.nh_inputFile = playprivatefile;
841 private.nh_input = pcap_open_offline(playprivatefile, errbuf);
842 if(private.nh_input == NULL) {
843 fprintf(stderr, "pcap_open_offline: %s\n", errbuf);
848 printf("%s: will exit on empty: %s\n", progname,
849 exitonempty ? "yes" : "no ");
851 if(signal(SIGINT, sig_handler) < 0)
852 perror("Setting handler for SIGINT");
858 printf("%s attached to unix sockets \n\t'%s,%s'\n and \n\t'%s,%s'\n",
859 progname, public.ctl_socket_name, public.data_socket_name,
860 private.ctl_socket_name, private.data_socket_name);
864 if(isatty(0)) FD_SET(0, &perm_fds);
866 FD_SET(public.ctl_listen_fd, &perm_fds);
867 FD_SET(private.ctl_listen_fd, &perm_fds);
868 FD_SET(public.data_fd, &perm_fds);
869 FD_SET(private.data_fd, &perm_fds);
872 max_fd = MAX(max_fd, public.ctl_listen_fd);
873 max_fd = MAX(max_fd, private.ctl_listen_fd);
874 max_fd = MAX(max_fd, public.data_fd);
875 max_fd = MAX(max_fd, private.data_fd);
883 struct timeval tv, *waittime;
887 if(public.nh_input ||
897 printf("invoking select with %s %s %s",
898 waittime ? "waittime" : "no wait",
899 public.nh_input ? "public" : "no-public",
900 private.nh_input ? "private" : "no-priv");
904 n = select(max_fd + 1, &temp, NULL, NULL, waittime);
907 printf(" -> %d left %lu\n", n, tv.tv_usec);
914 if(waittime && tv.tv_usec == 0) {
925 struct pcap_pkthdr ph;
926 const u_char *packet;
928 memset(&ph, 0, sizeof(ph));
930 packet = pcap_next(nh->nh_input, &ph);
934 printf("%8s: inserting packet of len %d\n", nh->nh_name, ph.len);
935 forward_data(nh, (struct packet *)packet, ph.len);
939 publicturn = !publicturn;
941 if(public.nh_input == NULL &&
942 private.nh_input == NULL &&
949 if(FD_ISSET(0, &temp)){
950 n = read(0, buf, sizeof(buf));
952 perror("Reading from stdin");
956 printf("EOF on stdin, cleaning up and exiting\n");
961 else if(FD_ISSET(public.ctl_listen_fd, &temp)){
962 accept_connection(&public);
963 FD_CLR(public.ctl_listen_fd, &temp);
965 else if(FD_ISSET(private.ctl_listen_fd, &temp)){
966 accept_connection(&private);
967 FD_CLR(private.ctl_listen_fd, &temp);
970 if(FD_ISSET(public.data_fd, &temp)){
971 handle_data(&public);
972 FD_CLR(public.data_fd, &temp);
973 } else if(FD_ISSET(private.data_fd, &temp)){
974 handle_data(&private);
975 FD_CLR(private.data_fd, &temp);
978 handle_connections(&public, &temp, max_fd + 1);
979 handle_connections(&private,&temp, max_fd + 1);
988 * $Log: uml_netjig.c,v $
989 * Revision 1.8 2002/01/21 01:08:41 mcr
990 * do not die if -t option is provided, but tcpdump compiled out.
992 * Revision 1.7 2002/01/12 04:01:36 mcr
993 * another #ifdef NETDISSET for tcpdump_print access.
995 * Revision 1.6 2002/01/12 03:40:56 mcr
996 * missing #ifdef for on NETDISSECT call.
998 * Revision 1.5 2002/01/12 02:52:46 mcr
999 * added --debug option to replace #if 0.
1001 * Revision 1.4 2001/10/23 16:34:12 mcr
1002 * use "progname" instead of "prog"
1003 * fixed public/private confused variables in printf().
1004 * fixed bug in termination logic.
1006 * Revision 1.3 2001/10/14 00:27:10 mcr
1007 * added code to play pcap files to both public and private sides.
1010 * Revision 1.2 2001/10/12 20:54:02 mcr
1011 * documented environment variables
1013 * added --help and fixed Usage().
1015 * Revision 1.1 2001/10/08 22:54:05 mcr
1016 * uml_net program that handles two interfaces.
1017 * no support for pcap yet.
1022 * c-file-style: "linux"