1 /* 2 * iptunnel.c "ip tuntap" 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: David Woodhouse <David.Woodhouse (at) intel.com> 10 * 11 */ 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <unistd.h> 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <arpa/inet.h> 20 #include <sys/ioctl.h> 21 #include <linux/if.h> 22 #include <linux/if_tun.h> 23 #include <pwd.h> 24 #include <grp.h> 25 #include <fcntl.h> 26 #include <dirent.h> 27 #include <errno.h> 28 29 #include "rt_names.h" 30 #include "utils.h" 31 #include "ip_common.h" 32 33 #define TUNDEV "/dev/net/tun" 34 35 static void usage(void) __attribute__((noreturn)); 36 37 static void usage(void) 38 { 39 fprintf(stderr, "Usage: ip tuntap { add | del } [ dev PHYS_DEV ] \n"); 40 fprintf(stderr, " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"); 41 fprintf(stderr, " [ one_queue ] [ pi ] [ vnet_hdr ]\n"); 42 fprintf(stderr, "\n"); 43 fprintf(stderr, "Where: USER := { STRING | NUMBER }\n"); 44 fprintf(stderr, " GROUP := { STRING | NUMBER }\n"); 45 exit(-1); 46 } 47 48 static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid) 49 { 50 int fd; 51 int ret = -1; 52 53 #ifdef IFF_TUN_EXCL 54 ifr->ifr_flags |= IFF_TUN_EXCL; 55 #endif 56 57 fd = open(TUNDEV, O_RDWR); 58 if (fd < 0) { 59 perror("open"); 60 return -1; 61 } 62 if (ioctl(fd, TUNSETIFF, ifr)) { 63 perror("ioctl(TUNSETIFF)"); 64 goto out; 65 } 66 if (uid != -1 && ioctl(fd, TUNSETOWNER, uid)) { 67 perror("ioctl(TUNSETOWNER)"); 68 goto out; 69 } 70 if (gid != -1 && ioctl(fd, TUNSETGROUP, gid)) { 71 perror("ioctl(TUNSETGROUP)"); 72 goto out; 73 } 74 if (ioctl(fd, TUNSETPERSIST, 1)) { 75 perror("ioctl(TUNSETPERSIST)"); 76 goto out; 77 } 78 ret = 0; 79 out: 80 close(fd); 81 return ret; 82 } 83 84 static int tap_del_ioctl(struct ifreq *ifr) 85 { 86 int fd = open(TUNDEV, O_RDWR); 87 int ret = -1; 88 89 if (fd < 0) { 90 perror("open"); 91 return -1; 92 } 93 if (ioctl(fd, TUNSETIFF, ifr)) { 94 perror("ioctl(TUNSETIFF)"); 95 goto out; 96 } 97 if (ioctl(fd, TUNSETPERSIST, 0)) { 98 perror("ioctl(TUNSETPERSIST)"); 99 goto out; 100 } 101 ret = 0; 102 out: 103 close(fd); 104 return ret; 105 106 } 107 static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid) 108 { 109 int count = 0; 110 111 memset(ifr, 0, sizeof(*ifr)); 112 113 ifr->ifr_flags |= IFF_NO_PI; 114 115 while (argc > 0) { 116 if (matches(*argv, "mode") == 0) { 117 NEXT_ARG(); 118 if (matches(*argv, "tun") == 0) { 119 if (ifr->ifr_flags & IFF_TAP) { 120 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); 121 exit(-1); 122 } 123 ifr->ifr_flags |= IFF_TUN; 124 } else if (matches(*argv, "tap") == 0) { 125 if (ifr->ifr_flags & IFF_TUN) { 126 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); 127 exit(-1); 128 } 129 ifr->ifr_flags |= IFF_TAP; 130 } else { 131 fprintf(stderr,"Cannot guess tunnel mode.\n"); 132 exit(-1); 133 } 134 } else if (uid && matches(*argv, "user") == 0) { 135 char *end; 136 unsigned long user; 137 138 NEXT_ARG(); 139 if (**argv && ((user = strtol(*argv, &end, 10)), !*end)) 140 *uid = user; 141 else { 142 struct passwd *pw = getpwnam(*argv); 143 if (!pw) { 144 fprintf(stderr, "invalid user \"%s\"\n", *argv); 145 exit(-1); 146 } 147 *uid = pw->pw_uid; 148 } 149 } else if (gid && matches(*argv, "group") == 0) { 150 char *end; 151 unsigned long group; 152 153 NEXT_ARG(); 154 155 if (**argv && ((group = strtol(*argv, &end, 10)), !*end)) 156 *gid = group; 157 else { 158 struct group *gr = getgrnam(*argv); 159 if (!gr) { 160 fprintf(stderr, "invalid group \"%s\"\n", *argv); 161 exit(-1); 162 } 163 *gid = gr->gr_gid; 164 } 165 } else if (matches(*argv, "pi") == 0) { 166 ifr->ifr_flags &= ~IFF_NO_PI; 167 } else if (matches(*argv, "one_queue") == 0) { 168 ifr->ifr_flags |= IFF_ONE_QUEUE; 169 } else if (matches(*argv, "vnet_hdr") == 0) { 170 ifr->ifr_flags |= IFF_VNET_HDR; 171 } else if (matches(*argv, "dev") == 0) { 172 NEXT_ARG(); 173 strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1); 174 } else { 175 if (matches(*argv, "name") == 0) { 176 NEXT_ARG(); 177 } else if (matches(*argv, "help") == 0) 178 usage(); 179 if (ifr->ifr_name[0]) 180 duparg2("name", *argv); 181 strncpy(ifr->ifr_name, *argv, IFNAMSIZ); 182 } 183 count++; 184 argc--; argv++; 185 } 186 187 return 0; 188 } 189 190 191 static int do_add(int argc, char **argv) 192 { 193 struct ifreq ifr; 194 uid_t uid = -1; 195 gid_t gid = -1; 196 197 if (parse_args(argc, argv, &ifr, &uid, &gid) < 0) 198 return -1; 199 200 if (!(ifr.ifr_flags & TUN_TYPE_MASK)) { 201 fprintf(stderr, "You failed to specify a tunnel mode\n"); 202 return -1; 203 } 204 return tap_add_ioctl(&ifr, uid, gid); 205 } 206 207 static int do_del(int argc, char **argv) 208 { 209 struct ifreq ifr; 210 211 if (parse_args(argc, argv, &ifr, NULL, NULL) < 0) 212 return -1; 213 214 return tap_del_ioctl(&ifr); 215 } 216 217 static int read_prop(char *dev, char *prop, long *value) 218 { 219 char fname[IFNAMSIZ+25], buf[80], *endp; 220 ssize_t len; 221 int fd; 222 long result; 223 224 sprintf(fname, "/sys/class/net/%s/%s", dev, prop); 225 fd = open(fname, O_RDONLY); 226 if (fd < 0) { 227 if (strcmp(prop, "tun_flags")) 228 fprintf(stderr, "open %s: %s\n", fname, 229 strerror(errno)); 230 return -1; 231 } 232 len = read(fd, buf, sizeof(buf)-1); 233 close(fd); 234 if (len < 0) { 235 fprintf(stderr, "read %s: %s", fname, strerror(errno)); 236 return -1; 237 } 238 239 buf[len] = 0; 240 result = strtol(buf, &endp, 0); 241 if (*endp != '\n') { 242 fprintf(stderr, "Failed to parse %s\n", fname); 243 return -1; 244 } 245 *value = result; 246 return 0; 247 } 248 249 static void print_flags(long flags) 250 { 251 if (flags & IFF_TUN) 252 printf(" tun"); 253 254 if (flags & IFF_TAP) 255 printf(" tap"); 256 257 if (!(flags & IFF_NO_PI)) 258 printf(" pi"); 259 260 if (flags & IFF_ONE_QUEUE) 261 printf(" one_queue"); 262 263 if (flags & IFF_VNET_HDR) 264 printf(" vnet_hdr"); 265 266 flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR); 267 if (flags) 268 printf(" UNKNOWN_FLAGS:%lx", flags); 269 } 270 271 static int do_show(int argc, char **argv) 272 { 273 DIR *dir; 274 struct dirent *d; 275 long flags, owner = -1, group = -1; 276 277 dir = opendir("/sys/class/net"); 278 if (!dir) { 279 perror("opendir"); 280 return -1; 281 } 282 while ((d = readdir(dir))) { 283 if (d->d_name[0] == '.' && 284 (d->d_name[1] == 0 || d->d_name[1] == '.')) 285 continue; 286 287 if (read_prop(d->d_name, "tun_flags", &flags)) 288 continue; 289 290 read_prop(d->d_name, "owner", &owner); 291 read_prop(d->d_name, "group", &group); 292 293 printf("%s:", d->d_name); 294 print_flags(flags); 295 if (owner != -1) 296 printf(" user %ld", owner); 297 if (group != -1) 298 printf(" group %ld", group); 299 printf("\n"); 300 } 301 closedir(dir); 302 return 0; 303 } 304 305 int do_iptuntap(int argc, char **argv) 306 { 307 if (argc > 0) { 308 if (matches(*argv, "add") == 0) 309 return do_add(argc-1, argv+1); 310 if (matches(*argv, "del") == 0) 311 return do_del(argc-1, argv+1); 312 if (matches(*argv, "show") == 0 || 313 matches(*argv, "lst") == 0 || 314 matches(*argv, "list") == 0) 315 return do_show(argc-1, argv+1); 316 if (matches(*argv, "help") == 0) 317 usage(); 318 } else 319 return do_show(0, NULL); 320 321 fprintf(stderr, "Command \"%s\" is unknown, try \"ip tuntap help\".\n", 322 *argv); 323 exit(-1); 324 } 325