tunctl.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <sys/ioctl.h>
  7. #include <net/if.h>
  8. #include <linux/if_tun.h>
  9. #include <pwd.h>
  10. #include <grp.h>
  11. #include <misc/version.h>
  12. #include <misc/open_standard_streams.h>
  13. #define PROGRAM_NAME "tunctl"
  14. #define TUN_DEVNODE "/dev/net/tun"
  15. struct {
  16. int help;
  17. int version;
  18. int op;
  19. char *device_name;
  20. char *user;
  21. char *group;
  22. } options;
  23. #define OP_MKTUN 1
  24. #define OP_MKTAP 2
  25. #define OP_RMTUN 3
  26. #define OP_RMTAP 4
  27. static void print_help (const char *name);
  28. static void print_version (void);
  29. static int parse_arguments (int argc, char *argv[]);
  30. static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group);
  31. static int remove_tuntap (const char *ifname, int is_tun);
  32. int main (int argc, char *argv[])
  33. {
  34. int res = 1;
  35. // open standard streams
  36. open_standard_streams();
  37. // parse command-line arguments
  38. if (!parse_arguments(argc, argv)) {
  39. fprintf(stderr, "Error: Failed to parse arguments\n");
  40. print_help(argv[0]);
  41. goto fail0;
  42. }
  43. // handle --help and --version
  44. if (options.help) {
  45. print_version();
  46. print_help(argv[0]);
  47. return 0;
  48. }
  49. if (options.version) {
  50. print_version();
  51. return 0;
  52. }
  53. if (options.op == OP_MKTUN || options.op == OP_MKTAP) {
  54. if (!options.user && !options.group) {
  55. fprintf(stderr, "WARNING: with neither --user nor --group, anyone will be able to use the device!\n");
  56. }
  57. res = !make_tuntap(options.device_name, options.op == OP_MKTUN, options.user, options.group);
  58. } else {
  59. res = !remove_tuntap(options.device_name, options.op == OP_RMTUN);
  60. }
  61. fail0:
  62. return res;
  63. }
  64. void print_help (const char *name)
  65. {
  66. printf(
  67. "Usage:\n"
  68. " %s [--help] [--version]\n"
  69. " %s --mktun <device_name> [--user <username>] [--group <groupname>]\n"
  70. " %s --mktap <device_name> [--user <username>] [--group <groupname>]\n"
  71. " %s --rmtun <device_name>\n"
  72. " %s --rmtap <device_name>\n",
  73. name, name, name, name, name
  74. );
  75. }
  76. void print_version (void)
  77. {
  78. printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
  79. }
  80. int parse_arguments (int argc, char *argv[])
  81. {
  82. if (argc <= 0) {
  83. return 0;
  84. }
  85. options.help = 0;
  86. options.version = 0;
  87. options.op = -1;
  88. options.device_name = NULL;
  89. options.user = NULL;
  90. options.group = NULL;
  91. for (int i = 1; i < argc; i++) {
  92. char *arg = argv[i];
  93. if (!strcmp(arg, "--help")) {
  94. options.help = 1;
  95. }
  96. else if (!strcmp(arg, "--version")) {
  97. options.version = 1;
  98. }
  99. else if (!strcmp(arg, "--mktun")) {
  100. if (1 >= argc - i) {
  101. fprintf(stderr, "%s: requires an argument\n", arg);
  102. return 0;
  103. }
  104. if (options.op >= 0) {
  105. fprintf(stderr, "%s: can only do one operation\n", arg);
  106. return 0;
  107. }
  108. options.op = OP_MKTUN;
  109. options.device_name = argv[i + 1];
  110. i++;
  111. }
  112. else if (!strcmp(arg, "--mktap")) {
  113. if (1 >= argc - i) {
  114. fprintf(stderr, "%s: requires an argument\n", arg);
  115. return 0;
  116. }
  117. if (options.op >= 0) {
  118. fprintf(stderr, "%s: can only do one operation\n", arg);
  119. return 0;
  120. }
  121. options.op = OP_MKTAP;
  122. options.device_name = argv[i + 1];
  123. i++;
  124. }
  125. else if (!strcmp(arg, "--rmtun")) {
  126. if (1 >= argc - i) {
  127. fprintf(stderr, "%s: requires an argument\n", arg);
  128. return 0;
  129. }
  130. if (options.op >= 0) {
  131. fprintf(stderr, "%s: can only do one operation\n", arg);
  132. return 0;
  133. }
  134. options.op = OP_RMTUN;
  135. options.device_name = argv[i + 1];
  136. i++;
  137. }
  138. else if (!strcmp(arg, "--rmtap")) {
  139. if (1 >= argc - i) {
  140. fprintf(stderr, "%s: requires an argument\n", arg);
  141. return 0;
  142. }
  143. if (options.op >= 0) {
  144. fprintf(stderr, "%s: can only do one operation\n", arg);
  145. return 0;
  146. }
  147. options.op = OP_RMTAP;
  148. options.device_name = argv[i + 1];
  149. i++;
  150. }
  151. else if (!strcmp(arg, "--user")) {
  152. if (1 >= argc - i) {
  153. fprintf(stderr, "%s: requires an argument\n", arg);
  154. return 0;
  155. }
  156. options.user = argv[i + 1];
  157. i++;
  158. }
  159. else if (!strcmp(arg, "--group")) {
  160. if (1 >= argc - i) {
  161. fprintf(stderr, "%s: requires an argument\n", arg);
  162. return 0;
  163. }
  164. options.group = argv[i + 1];
  165. i++;
  166. }
  167. else {
  168. fprintf(stderr, "unknown option: %s\n", arg);
  169. return 0;
  170. }
  171. }
  172. if (options.help || options.version) {
  173. return 1;
  174. }
  175. if (options.op < 0) {
  176. fprintf(stderr, "--mktun, --mktap --rmtun or --rmtap is required\n");
  177. return 0;
  178. }
  179. if ((options.user || options.group) && options.op != OP_MKTUN && options.op != OP_MKTAP) {
  180. fprintf(stderr, "--user and --group only make sense for --mktun and --mktap\n");
  181. return 0;
  182. }
  183. return 1;
  184. }
  185. static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group)
  186. {
  187. int res = 0;
  188. if (strlen(ifname) >= IFNAMSIZ) {
  189. fprintf(stderr, "Error: ifname too long\n");
  190. goto fail0;
  191. }
  192. int fd = open(TUN_DEVNODE, O_RDWR);
  193. if (fd < 0) {
  194. perror("open");
  195. fprintf(stderr, "Error: open tun failed\n");
  196. goto fail0;
  197. }
  198. struct ifreq ifr;
  199. memset(&ifr, 0, sizeof(ifr));
  200. ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP);
  201. snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname);
  202. if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
  203. perror("ioctl(TUNSETIFF)");
  204. fprintf(stderr, "Error: TUNSETIFF failed\n");
  205. goto fail1;
  206. }
  207. uid_t uid = -1;
  208. gid_t gid = -1;
  209. if (user) {
  210. long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
  211. if (bufsize < 0) {
  212. bufsize = 16384;
  213. }
  214. char *buf = malloc(bufsize);
  215. if (!buf) {
  216. fprintf(stderr, "Error: malloc failed\n");
  217. goto fail1;
  218. }
  219. struct passwd pwd;
  220. struct passwd *pwd_res;
  221. getpwnam_r(user, &pwd, buf, bufsize, &pwd_res);
  222. if (!pwd_res) {
  223. fprintf(stderr, "Error: getpwnam_r failed\n");
  224. free(buf);
  225. goto fail1;
  226. }
  227. uid = pwd.pw_uid;
  228. free(buf);
  229. }
  230. if (group) {
  231. long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
  232. if (bufsize < 0) {
  233. bufsize = 16384;
  234. }
  235. char *buf = malloc(bufsize);
  236. if (!buf) {
  237. fprintf(stderr, "Error: malloc failed\n");
  238. goto fail1;
  239. }
  240. struct group grp;
  241. struct group *grp_res;
  242. getgrnam_r(group, &grp, buf, bufsize, &grp_res);
  243. if (!grp_res) {
  244. fprintf(stderr, "Error: getgrnam_r failed\n");
  245. free(buf);
  246. goto fail1;
  247. }
  248. gid = grp.gr_gid;
  249. free(buf);
  250. }
  251. if (ioctl(fd, TUNSETOWNER, uid) < 0) {
  252. perror("ioctl(TUNSETOWNER)");
  253. fprintf(stderr, "Error: TUNSETOWNER failed\n");
  254. goto fail1;
  255. }
  256. if (ioctl(fd, TUNSETGROUP, gid) < 0) {
  257. perror("ioctl(TUNSETGROUP)");
  258. fprintf(stderr, "Error: TUNSETGROUP failed\n");
  259. goto fail1;
  260. }
  261. if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) {
  262. perror("ioctl(TUNSETPERSIST)");
  263. fprintf(stderr, "Error: TUNSETPERSIST failed\n");
  264. goto fail1;
  265. }
  266. res = 1;
  267. fail1:
  268. close(fd);
  269. fail0:
  270. return res;
  271. }
  272. static int remove_tuntap (const char *ifname, int is_tun)
  273. {
  274. int res = 0;
  275. if (strlen(ifname) >= IFNAMSIZ) {
  276. fprintf(stderr, "Error: ifname too long\n");
  277. goto fail0;
  278. }
  279. int fd = open(TUN_DEVNODE, O_RDWR);
  280. if (fd < 0) {
  281. perror("open");
  282. fprintf(stderr, "Error: open tun failed\n");
  283. goto fail0;
  284. }
  285. struct ifreq ifr;
  286. memset(&ifr, 0, sizeof(ifr));
  287. ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP);
  288. snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname);
  289. if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
  290. perror("ioctl(TUNSETIFF)");
  291. fprintf(stderr, "Error: TUNSETIFF failed\n");
  292. goto fail1;
  293. }
  294. if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) {
  295. perror("ioctl(TUNSETPERSIST)");
  296. fprintf(stderr, "Error: TUNSETPERSIST failed\n");
  297. goto fail1;
  298. }
  299. res = 1;
  300. fail1:
  301. close(fd);
  302. fail0:
  303. return res;
  304. }