/* * Quicktun tunnel driver - userspace cli tool * Copyright (C) 2011 Matthias Schiffer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include "quicktun.h" static void usage() { fputs("Usage: qtctl show [ dev NAME ]\n" " qtctl add [ dev NAME ] [ mode { ethernet | ip } ]\n" " [ local_address ADDRESS ] [ local_port PORT ]\n" " [ remote_address ADDRESS ] [ remote_port PORT ]\n" " [ remote_float { on | off } ]\n" " qtctl change dev NAME [ remote_address ADDRESS ] [ remote_port PORT ]\n" " [ remote_float { on | off } ]\n" " qtctl del dev NAME\n", stderr); exit(2); } static int print_error(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg) { fprintf(stderr, "Error received: %s\n", strerror(-nlerr->error)); exit(1); } static void add(int argc, char *argv[], struct nl_handle *sock, int family) { struct nl_msg *msg; struct nl_cb *cb; int p = 0; uint16_t mode = QUICKTUN_MODE_ETHERNET; char *tmp_charp; struct in_addr tmp_addr; unsigned long tmp_ul; msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, QUICKTUN_CMD_CREATE_DEVICE, 1); while ((++p) < argc) { if (!strcmp(argv[p], "dev")) { if ((++p) >= argc) usage(); tmp_charp = strndup(argv[p], IFNAMSIZ - 1); nla_put_string(msg, QUICKTUN_A_IFNAME, tmp_charp); free(tmp_charp); } else if (!strcmp(argv[p], "mode")) { if ((++p) >= argc) usage(); if (!strcmp(argv[p], "ethernet")) mode = QUICKTUN_MODE_ETHERNET; else if (!strcmp(argv[p], "ip")) mode = QUICKTUN_MODE_IP; else usage(); } else if (!strcmp(argv[p], "local_address")) { if ((++p) >= argc) usage(); if (!(inet_aton(argv[p], &tmp_addr))) usage(); nla_put_u32(msg, QUICKTUN_A_LOCAL_ADDRESS, tmp_addr.s_addr); } else if (!strcmp(argv[p], "local_port")) { if ((++p) >= argc) usage(); tmp_ul = strtoul(argv[p], &tmp_charp, 10); if (*tmp_charp) usage(); nla_put_u16(msg, QUICKTUN_A_LOCAL_PORT, htons((short)tmp_ul)); } else if (!strcmp(argv[p], "remote_address")) { if ((++p) >= argc) usage(); if (!(inet_aton(argv[p], &tmp_addr))) usage(); nla_put_u32(msg, QUICKTUN_A_REMOTE_ADDRESS, tmp_addr.s_addr); } else if (!strcmp(argv[p], "remote_port")) { if ((++p) >= argc) usage(); tmp_ul = strtoul(argv[p], &tmp_charp, 10); if (*tmp_charp) usage(); nla_put_u16(msg, QUICKTUN_A_REMOTE_PORT, htons((short)tmp_ul)); } else if (!strcmp(argv[p], "remote_float")) { if ((++p) >= argc) usage(); if (!strcmp(argv[p], "on")) nla_put_u8(msg, QUICKTUN_A_REMOTE_FLOAT, 1); else if (!strcmp(argv[p], "off")) nla_put_u8(msg, QUICKTUN_A_REMOTE_FLOAT, 0); else usage(); } else usage(); } nla_put_u16(msg, QUICKTUN_A_MODE, mode); nl_send_auto_complete(sock, msg); nlmsg_free(msg); cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); } static void del(int argc, char *argv[], struct nl_handle *sock, int family) { struct nl_msg *msg; struct nl_cb *cb; int p = 0; char *tmp_charp; int has_dev = 0; msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, QUICKTUN_CMD_DELETE_DEVICE, 1); while ((++p) < argc) { if (!strcmp(argv[p], "dev")) { if ((++p) >= argc) usage(); if (has_dev) usage(); tmp_charp = strndup(argv[p], IFNAMSIZ - 1); nla_put_string(msg, QUICKTUN_A_IFNAME, tmp_charp); free(tmp_charp); has_dev = 1; } else usage(); } if (!has_dev) usage(); nl_send_auto_complete(sock, msg); nlmsg_free(msg); cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); } static void change(int argc, char *argv[], struct nl_handle *sock, int family) { struct nl_msg *msg; struct nl_cb *cb; int p = 0; char *tmp_charp; struct in_addr tmp_addr; unsigned long tmp_ul; int has_dev = 0; msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, QUICKTUN_CMD_CHANGE_DEVICE, 1); while ((++p) < argc) { if (!strcmp(argv[p], "dev")) { if ((++p) >= argc) usage(); if (has_dev) usage(); tmp_charp = strndup(argv[p], IFNAMSIZ - 1); nla_put_string(msg, QUICKTUN_A_IFNAME, tmp_charp); free(tmp_charp); has_dev = 1; } else if (!strcmp(argv[p], "remote_address")) { if ((++p) >= argc) usage(); if (!(inet_aton(argv[p], &tmp_addr))) usage(); nla_put_u32(msg, QUICKTUN_A_REMOTE_ADDRESS, tmp_addr.s_addr); } else if (!strcmp(argv[p], "remote_port")) { if ((++p) >= argc) usage(); tmp_ul = strtoul(argv[p], &tmp_charp, 10); if (*tmp_charp) usage(); nla_put_u16(msg, QUICKTUN_A_REMOTE_PORT, htons((short)tmp_ul)); } else if (!strcmp(argv[p], "remote_float")) { if ((++p) >= argc) usage(); if (!strcmp(argv[p], "on")) nla_put_u8(msg, QUICKTUN_A_REMOTE_FLOAT, 1); else if (!strcmp(argv[p], "off")) nla_put_u8(msg, QUICKTUN_A_REMOTE_FLOAT, 0); else usage(); } else usage(); } if (!has_dev) usage(); nl_send_auto_complete(sock, msg); nlmsg_free(msg); cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); } static int show_callback(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct genlmsghdr *ghdr; struct nlattr *pos, *pos2; struct nlattr *nested[__QUICKTUN_A_MAX]; struct in_addr addr; int rem; if (!genlmsg_valid_hdr(nlh, 0)) { fputs("Received invalid data from kernel.", stderr); exit(1); } ghdr = nlmsg_data(nlh); if (ghdr->cmd != QUICKTUN_CMD_GET_DEVICE) return NL_OK; nla_for_each_attr(pos, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), rem) { nla_parse_nested(nested, QUICKTUN_A_MAX, pos, NULL); if(!nested[QUICKTUN_A_IFNAME] || !nested[QUICKTUN_A_MODE] || !nested[QUICKTUN_A_LOCAL_ADDRESS] || !nested[QUICKTUN_A_LOCAL_PORT] || !nested[QUICKTUN_A_REMOTE_ADDRESS] || !nested[QUICKTUN_A_REMOTE_PORT] || !nested[QUICKTUN_A_REMOTE_FLOAT]) continue; printf("%s: %s ", nla_get_string(nested[QUICKTUN_A_IFNAME]), (nla_get_u16(nested[QUICKTUN_A_MODE]) == QUICKTUN_MODE_ETHERNET) ? "ethernet" : (nla_get_u16(nested[QUICKTUN_A_MODE]) == QUICKTUN_MODE_IP) ? "ip" : "unknown" ); addr.s_addr = nla_get_u32(nested[QUICKTUN_A_LOCAL_ADDRESS]); printf("local %s:%u ", inet_ntoa(addr), ntohs(nla_get_u16(nested[QUICKTUN_A_LOCAL_PORT]))); addr.s_addr = nla_get_u32(nested[QUICKTUN_A_REMOTE_ADDRESS]); printf("remote %s:%u ", inet_ntoa(addr), ntohs(nla_get_u16(nested[QUICKTUN_A_REMOTE_PORT]))); printf("remote_float %s\n", nla_get_u8(nested[QUICKTUN_A_REMOTE_FLOAT]) ? "on" : "off"); } return NL_STOP; } static void show(int argc, char *argv[], struct nl_handle *sock, int family) { struct nl_msg *msg; struct nl_cb *cb; int p = 0; char *tmp_charp; int has_dev = 0; msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO, QUICKTUN_CMD_GET_DEVICE, 1); while ((++p) < argc) { if (!strcmp(argv[p], "dev")) { if ((++p) >= argc) usage(); if (has_dev) usage(); tmp_charp = strndup(argv[p], IFNAMSIZ - 1); nla_put_string(msg, QUICKTUN_A_IFNAME, tmp_charp); free(tmp_charp); has_dev = 1; } } nl_send_auto_complete(sock, msg); nlmsg_free(msg); cb = nl_cb_alloc(NL_CB_DEFAULT); nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, show_callback, NULL); nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); nl_recvmsgs(sock, cb); } int main(int argc, char *argv[]) { struct nl_handle *sock; int family; sock = nl_handle_alloc(); genl_connect(sock); family = genl_ctrl_resolve(sock, "quicktun"); if (family < 0) { fputs("Your kernel doesn't support Quicktun.\n", stderr); exit(1); } if (argc < 2) show(argc, argv, sock, family); else if (!strcmp(argv[1], "add")) add(argc-1, argv+1, sock, family); else if (!strcmp(argv[1], "del")) del(argc-1, argv+1, sock, family); else if (!strcmp(argv[1], "change")) change(argc-1, argv+1, sock, family); else if (!strcmp(argv[1], "show")) show(argc-1, argv+1, sock, family); else usage(); return 0; }