Folks, I am pulling what little hair I have left out over trying to read
packets from the TUN/TAP interface on uCLinux.

I have the following code that creates my tap0 interface..

/*
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/if_tun.h>

/* TUNSETGROUP appeared in 2.6.23 */
#ifndef TUNSETGROUP
#define TUNSETGROUP   _IOW('T', 206, int)
#endif

static void Usage(char *name)
{
    fprintf(stderr, "Create: %s [-b] [-u owner] [-g group] [-t device-name]
"
    "[-f tun-clone-device]\n", name);
    fprintf(stderr, "Delete: %s -d device-name [-f tun-clone-device]\n\n",
    name);
    fprintf(stderr, "The default tun clone device is /dev/net/tun - some
systems"
    " use\n/dev/misc/net/tun instead\n\n");
    fprintf(stderr, "-b will result in brief output (just the device
name)\n");
    exit(1);
}

int main(int argc, char **argv)
{
    struct ifreq ifr;
    struct passwd *pw;
    struct group *gr;
    uid_t owner = -1;
    gid_t group = -1;
    int tap_fd, opt, delete = 0, brief = 0;
    char *tun = "", *file = "/dev/net/tun", *name = argv[0], *end;

    while ((opt = getopt(argc, argv, "bd:f:t:u:g:")) > 0) {
        switch (opt) {
        case 'b':
            brief = 1;
            break;
        case 'd':
            delete = 1;
            tun = optarg;
            break;
        case 'f':
            file = optarg;
            break;
        case 'u':
            pw = getpwnam(optarg);
            if (pw != NULL) {
                owner = pw->pw_uid;
                break;
            }
            owner = strtol(optarg, &end, 0);
            if (*end != '\0') {
                fprintf(stderr, "'%s' is neither a username nor a numeric
uid.\n",
                optarg);
                Usage(name);
            }
            break;
        case 'g':
            gr = getgrnam(optarg);
            if (gr != NULL) {
                group = gr->gr_gid;
                break;
            }
            group = strtol(optarg, &end, 0);
            if (*end != '\0') {
                fprintf(stderr, "'%s' is neither a groupname nor a numeric
group.\n",
                optarg);
                Usage(name);
            }
            break;

        case 't':
            tun = optarg;
            break;
        case 'h':
        default:
            Usage(name);
        }
    }

    argv += optind;
    argc -= optind;

    if (argc > 0)
        Usage(name);

    if ((tap_fd = open(file, O_RDWR)) < 0) {
        fprintf(stderr, "Failed to open '%s' : ", file);
        perror("");
        exit(1);
    }

    memset(&ifr, 0, sizeof(ifr));

    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
    strncpy(ifr.ifr_name, tun, sizeof(ifr.ifr_name) - 1);
    if (ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0) {
        perror("TUNSETIFF");
        exit(1);
    }

    if (delete) {
        if (ioctl(tap_fd, TUNSETPERSIST, 0) < 0) {
            perror("disabling TUNSETPERSIST");
            exit(1);
        }
        printf("Set '%s' nonpersistent\n", ifr.ifr_name);
    } else {
        /* emulate behaviour prior to TUNSETGROUP */
        if (owner == (uid_t) - 1 && group == (gid_t) - 1) {
            owner = geteuid();
        }

        if (owner != (uid_t) - 1) {
            if (ioctl(tap_fd, TUNSETOWNER, owner) < 0) {
                perror("TUNSETOWNER");
                exit(1);
            }
        }
        if (group != (gid_t) - 1) {
            if (ioctl(tap_fd, TUNSETGROUP, group) < 0) {
                perror("TUNSETGROUP");
                exit(1);
            }
        }

        if (ioctl(tap_fd, TUNSETPERSIST, 1) < 0) {
            perror("enabling TUNSETPERSIST");
            exit(1);
        }

        if (brief)
            printf("%s\n", ifr.ifr_name);
        else {
            printf("Set '%s' persistent and owned by", ifr.ifr_name);
            if (owner != (uid_t) - 1)
                printf(" uid %d", owner);
            if (group != (gid_t) - 1)
                printf(" gid %d", group);
            printf("\n");
        }
    }
    return(0);
}

Pretty standard stuff....

I create my TAP interface using the following command.

/tmp # ./tunctl -t tap0

Then I plumb the interface using the following command.

/tmp # ifconfig tap0 inet 192.168.0.65 -arp

At this point, everyting looks good.. I can ping the tap0 interface and it
seems to work correctly. I can also ssh into my uclinux box over the tap0
iinterface.

But, the following code works great on standard Linux but will not work
correctly on my uclinux box. It seems to hang at the read() call and
nothing gets returned..

/*
 *
 */
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int tun_alloc(char *dev, int flags)
{
    struct ifreq ifr;
    int fd, err;
    char *clonedev = "/dev/net/tun";

    /* Arguments taken by the function:
     *
     * char *dev: the name of an interface (or '\0'). MUST have enough
     *   space to hold the interface name if '\0' is passed
     * int flags: interface flags (eg, IFF_TUN etc.)
     */

    /* open the clone device */
    if ((fd = open(clonedev, O_RDWR | O_NONBLOCK)) < 0) {
        fprintf(stderr, "Tunnel open.. %d", errno);
        return fd;
    }

    /* preparation of the struct ifr, of type "struct ifreq" */
    memset(&ifr, 0, sizeof(ifr));

    ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */

    if (*dev) {
        /* if a device name was specified, put it in the structure;
otherwise,
         * the kernel will try to allocate the "next" device of the
         * specified type */
        strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    }

    /* try to create the device */
    if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) == -1) {
        fprintf(stderr, "Tunnel set if.. %d", err);
        close(fd);
        return err;
    }

    /* if the operation was successful, write back the name of the
     * interface to the variable "dev", so the caller can know
     * it. Note that the caller MUST reserve space in *dev (see calling
     * code below) */
    strcpy(dev, ifr.ifr_name);

    printf("fd = %d\n", fd);
    /* this is the special file descriptor that the caller will use to talk
     * with the virtual interface */
    return fd;
}

int main(int argc, char **argv)
{
    int tuntap_fd;
    unsigned int features;
    int nread;

    unsigned char buffer[2048];
    char tun_name[IFNAMSIZ];

    fd_set readfs;
    int rval;

    /* Connect to the device */
    strcpy(tun_name, "tap0");
    tuntap_fd = tun_alloc(tun_name, IFF_TAP | IFF_NO_PI); /* tun interface
*/
    if (tuntap_fd == -1) {
        fprintf(stderr, "tun_alloc: error %d\n", tuntap_fd);
        exit(tuntap_fd);
    }

    if (ioctl(tuntap_fd, TUNGETFEATURES, (void *) &features) == -1) {
        perror("Tunnel get features");
        close(tuntap_fd);
        return errno;
    }

    printf("Device %s features = 0x%x\n", tun_name, features);


    while (1) {
        FD_ZERO(&readfs);
        FD_SET(tuntap_fd, &readfs); /* set testing for source tun/tap */

        printf("starting select..\n");
        /* block until input becomes available */
        select(tuntap_fd + 1, &readfs, NULL, NULL, NULL);
        printf("finished select..\n");

        if (FD_ISSET(tuntap_fd, &readfs)) {
            if ((rval = read(tuntap_fd, buffer, sizeof(buffer))) > 0) {
                fprintf(stdout, "read %d bytes\n", rval);
                fflush(stdout);
            }
        }

    }

    exit(0);
}


Any suggestions about why this is acting this way and how to fix it are
MOST APPRECIATED!!

Thanks.

-brad w.
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to