| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <net/if.h>
- #include <linux/if_tun.h>
- #include <pwd.h>
- #include <grp.h>
- #include <misc/version.h>
- #include <misc/open_standard_streams.h>
- #define PROGRAM_NAME "tunctl"
- #define TUN_DEVNODE "/dev/net/tun"
- struct {
- int help;
- int version;
- int op;
- char *device_name;
- char *user;
- char *group;
- } options;
- #define OP_MKTUN 1
- #define OP_MKTAP 2
- #define OP_RMTUN 3
- #define OP_RMTAP 4
- static void print_help (const char *name);
- static void print_version (void);
- static int parse_arguments (int argc, char *argv[]);
- static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group);
- static int remove_tuntap (const char *ifname, int is_tun);
- int main (int argc, char *argv[])
- {
- int res = 1;
-
- // open standard streams
- open_standard_streams();
-
- // parse command-line arguments
- if (!parse_arguments(argc, argv)) {
- fprintf(stderr, "Error: Failed to parse arguments\n");
- print_help(argv[0]);
- goto fail0;
- }
-
- // handle --help and --version
- if (options.help) {
- print_version();
- print_help(argv[0]);
- return 0;
- }
- if (options.version) {
- print_version();
- return 0;
- }
-
- if (options.op == OP_MKTUN || options.op == OP_MKTAP) {
- if (!options.user && !options.group) {
- fprintf(stderr, "WARNING: with neither --user nor --group, anyone will be able to use the device!\n");
- }
- res = !make_tuntap(options.device_name, options.op == OP_MKTUN, options.user, options.group);
- } else {
- res = !remove_tuntap(options.device_name, options.op == OP_RMTUN);
- }
-
- fail0:
- return res;
- }
- void print_help (const char *name)
- {
- printf(
- "Usage:\n"
- " %s [--help] [--version]\n"
- " %s --mktun <device_name> [--user <username>] [--group <groupname>]\n"
- " %s --mktap <device_name> [--user <username>] [--group <groupname>]\n"
- " %s --rmtun <device_name>\n"
- " %s --rmtap <device_name>\n",
- name, name, name, name, name
- );
- }
- void print_version (void)
- {
- printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
- }
- int parse_arguments (int argc, char *argv[])
- {
- if (argc <= 0) {
- return 0;
- }
-
- options.help = 0;
- options.version = 0;
- options.op = -1;
- options.device_name = NULL;
- options.user = NULL;
- options.group = NULL;
-
- for (int i = 1; i < argc; i++) {
- char *arg = argv[i];
- if (!strcmp(arg, "--help")) {
- options.help = 1;
- }
- else if (!strcmp(arg, "--version")) {
- options.version = 1;
- }
- else if (!strcmp(arg, "--mktun")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- if (options.op >= 0) {
- fprintf(stderr, "%s: can only do one operation\n", arg);
- return 0;
- }
- options.op = OP_MKTUN;
- options.device_name = argv[i + 1];
- i++;
- }
- else if (!strcmp(arg, "--mktap")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- if (options.op >= 0) {
- fprintf(stderr, "%s: can only do one operation\n", arg);
- return 0;
- }
- options.op = OP_MKTAP;
- options.device_name = argv[i + 1];
- i++;
- }
- else if (!strcmp(arg, "--rmtun")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- if (options.op >= 0) {
- fprintf(stderr, "%s: can only do one operation\n", arg);
- return 0;
- }
- options.op = OP_RMTUN;
- options.device_name = argv[i + 1];
- i++;
- }
- else if (!strcmp(arg, "--rmtap")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- if (options.op >= 0) {
- fprintf(stderr, "%s: can only do one operation\n", arg);
- return 0;
- }
- options.op = OP_RMTAP;
- options.device_name = argv[i + 1];
- i++;
- }
- else if (!strcmp(arg, "--user")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- options.user = argv[i + 1];
- i++;
- }
- else if (!strcmp(arg, "--group")) {
- if (1 >= argc - i) {
- fprintf(stderr, "%s: requires an argument\n", arg);
- return 0;
- }
- options.group = argv[i + 1];
- i++;
- }
- else {
- fprintf(stderr, "unknown option: %s\n", arg);
- return 0;
- }
- }
-
- if (options.help || options.version) {
- return 1;
- }
-
- if (options.op < 0) {
- fprintf(stderr, "--mktun, --mktap --rmtun or --rmtap is required\n");
- return 0;
- }
-
- if ((options.user || options.group) && options.op != OP_MKTUN && options.op != OP_MKTAP) {
- fprintf(stderr, "--user and --group only make sense for --mktun and --mktap\n");
- return 0;
- }
-
- return 1;
- }
- static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group)
- {
- int res = 0;
-
- if (strlen(ifname) >= IFNAMSIZ) {
- fprintf(stderr, "Error: ifname too long\n");
- goto fail0;
- }
-
- int fd = open(TUN_DEVNODE, O_RDWR);
- if (fd < 0) {
- perror("open");
- fprintf(stderr, "Error: open tun failed\n");
- goto fail0;
- }
-
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP);
- snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname);
-
- if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
- perror("ioctl(TUNSETIFF)");
- fprintf(stderr, "Error: TUNSETIFF failed\n");
- goto fail1;
- }
-
- uid_t uid = -1;
- gid_t gid = -1;
-
- if (user) {
- long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize < 0) {
- bufsize = 16384;
- }
-
- char *buf = malloc(bufsize);
- if (!buf) {
- fprintf(stderr, "Error: malloc failed\n");
- goto fail1;
- }
-
- struct passwd pwd;
- struct passwd *res;
- getpwnam_r(user, &pwd, buf, bufsize, &res);
- if (!res) {
- fprintf(stderr, "Error: getpwnam_r failed\n");
- free(buf);
- goto fail1;
- }
-
- uid = pwd.pw_uid;
- free(buf);
- }
-
- if (group) {
- long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
- if (bufsize < 0) {
- bufsize = 16384;
- }
-
- char *buf = malloc(bufsize);
- if (!buf) {
- fprintf(stderr, "Error: malloc failed\n");
- goto fail1;
- }
-
- struct group grp;
- struct group *res;
- getgrnam_r(group, &grp, buf, bufsize, &res);
- if (!res) {
- fprintf(stderr, "Error: getgrnam_r failed\n");
- free(buf);
- goto fail1;
- }
-
- gid = grp.gr_gid;
- free(buf);
- }
-
- if (ioctl(fd, TUNSETOWNER, uid) < 0) {
- perror("ioctl(TUNSETOWNER)");
- fprintf(stderr, "Error: TUNSETOWNER failed\n");
- goto fail1;
- }
-
- if (ioctl(fd, TUNSETGROUP, gid) < 0) {
- perror("ioctl(TUNSETGROUP)");
- fprintf(stderr, "Error: TUNSETGROUP failed\n");
- goto fail1;
- }
-
- if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) {
- perror("ioctl(TUNSETPERSIST)");
- fprintf(stderr, "Error: TUNSETPERSIST failed\n");
- goto fail1;
- }
-
- res = 1;
-
- fail1:
- close(fd);
- fail0:
- return res;
- }
- static int remove_tuntap (const char *ifname, int is_tun)
- {
- int res = 0;
-
- if (strlen(ifname) >= IFNAMSIZ) {
- fprintf(stderr, "Error: ifname too long\n");
- goto fail0;
- }
-
- int fd = open(TUN_DEVNODE, O_RDWR);
- if (fd < 0) {
- perror("open");
- fprintf(stderr, "Error: open tun failed\n");
- goto fail0;
- }
-
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP);
- snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname);
-
- if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
- perror("ioctl(TUNSETIFF)");
- fprintf(stderr, "Error: TUNSETIFF failed\n");
- goto fail1;
- }
-
- if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) {
- perror("ioctl(TUNSETPERSIST)");
- fprintf(stderr, "Error: TUNSETPERSIST failed\n");
- goto fail1;
- }
-
- res = 1;
-
- fail1:
- close(fd);
- fail0:
- return res;
- }
|