From: darkcoven Date: Sun, 20 Oct 2013 16:10:54 +0000 (+0200) Subject: Add basic switch emulator X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=61b44dfb3d59cc22a44c083b88ebb9969bb7fe05;p=ngadmin Add basic switch emulator --- diff --git a/.gitignore b/.gitignore index e4c652d..4164e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.pc ngcli ngspy +ngemu doxyfile lib/doc/ raw/doc/ diff --git a/emu/src/Makefile.am b/emu/src/Makefile.am index e69de29..d8fbef6 100644 --- a/emu/src/Makefile.am +++ b/emu/src/Makefile.am @@ -0,0 +1,7 @@ + +bin_PROGRAMS = ngemu + +ngemu_SOURCES = emu.c +ngemu_CPPFLAGS = -I$(top_srcdir)/raw/include/ -I$(top_srcdir)/lib/include/ +ngemu_LDADD = $(top_builddir)/raw/src/librawnsdp.la + diff --git a/emu/src/emu.c b/emu/src/emu.c new file mode 100644 index 0000000..d90bcb8 --- /dev/null +++ b/emu/src/emu.c @@ -0,0 +1,447 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include /* FIXME */ +#include +#include +#include +#include + + +#define MAX_STR_SIZE 64 + +struct swi_info { + char product[MAX_STR_SIZE]; + char name[MAX_STR_SIZE]; + char firmware[MAX_STR_SIZE]; + char password[MAX_STR_SIZE]; + bool encpass; + unsigned char ports; + unsigned char mac[ETH_ALEN]; + in_addr_t ip; + in_addr_t netmask; + in_addr_t gw; + bool dhcp; +}; + + +static struct swi_info swi; +static int sock; + + +static int process_read_attr (struct nsdp_cmd *nc, List *attr, struct attr *at) +{ + unsigned char p, *byte; + unsigned short *word; + unsigned int *dword; + struct attr_port_status *apu; + struct attr_port_stat *api; + + + switch (at->attr) { + } + + at->size = 0; + free(at->data); + at->data = NULL; + + switch (at->attr) { + + case ATTR_PRODUCT: + byte = malloc(MAX_STR_SIZE); + memcpy(byte, swi.product, MAX_STR_SIZE); + at->data = byte; + at->size = MAX_STR_SIZE; + break; + + case ATTR_NAME: + byte = malloc(MAX_STR_SIZE); + memcpy(byte, swi.name, MAX_STR_SIZE); + at->data = byte; + at->size = MAX_STR_SIZE; + break; + + case ATTR_FIRM_VER: + byte = malloc(MAX_STR_SIZE); + memcpy(byte, swi.firmware, MAX_STR_SIZE); + at->data = byte; + at->size = MAX_STR_SIZE; + break; + + case ATTR_MAC: + byte = malloc(ETH_ALEN); + memcpy(byte, swi.mac, ETH_ALEN); + at->data = byte; + at->size = ETH_ALEN; + break; + + case ATTR_PORTS_COUNT: + byte = malloc(1); + *byte = swi.ports; + at->data = byte; + at->size = 1; + break; + + case ATTR_IP: + byte = malloc(sizeof(in_addr_t)); + *(in_addr_t*)byte = swi.ip; + at->data = byte; + at->size = sizeof(in_addr_t); + break; + + case ATTR_NETMASK: + byte = malloc(sizeof(in_addr_t)); + *(in_addr_t*)byte = swi.netmask; + at->data = byte; + at->size = sizeof(in_addr_t); + break; + + case ATTR_GATEWAY: + byte = malloc(sizeof(in_addr_t)); + *(in_addr_t*)byte = swi.gw; + at->data = byte; + at->size = sizeof(in_addr_t); + break; + + case ATTR_DHCP: + /* Note: DHCP attribute is special, it is 2 two bytes long + * when sent by the switch but only 1 byte long when sent + * by the client + */ + word = malloc(2); + *word = swi.dhcp; + at->data = word; + at->size = 2; + break; + + case ATTR_ENCPASS: + if (!swi.encpass) + break; + dword = malloc(4); + *dword = 1; + at->data = dword; + at->size = 4; + break; + + case ATTR_PORT_STATUS: + for (p = 1; p <= swi.ports; p++) { + apu = malloc(sizeof(*apu)); + apu->port = p; + apu->status = SPEED_1000; + apu->unk = 0; + + pushFrontList(attr, newAttr(ATTR_PORT_STATUS, sizeof(*apu), apu)); + } + return 1; + + case ATTR_PORT_STATISTICS: + for (p = 1; p <= swi.ports; p++) { + api = malloc(sizeof(*api)); + memset(api, 0, sizeof(*api)); + api->port = p; + api->recv = p * 100000; + api->sent = p * 200000; + + pushFrontList(attr, newAttr(ATTR_PORT_STATISTICS, sizeof(*api), api)); + } + return 1; + } + + return 0; +} + + +static int process_write_attr (struct nsdp_cmd *nc, List *attr, struct attr *at) +{ + char *text; + + + if (at->size == 0) + return -EMSGSIZE; + + switch (at->attr) { + + case ATTR_NEW_PASSWORD: + if (swi.encpass) + passwordEndecode(at->data, at->size); + text = at->data; + trim(text, MAX_STR_SIZE); + strncpy(swi.password, text, MAX_STR_SIZE); + break; + } + + return 1; +} + + +static int check_password (struct nsdp_cmd *nc, List *attr) +{ + ListNode *ln = attr->first; + struct attr *at = NULL; + char *text; + + + /* in a write request, the password attribute must be present + * and the first element of the list + * official win app never sends password inside a read request, but + * ngadmin does that because in that case the password is not echoed + * back by the switch, so we have to support that, though password in + * such requests are not mandatory + */ + if (ln == NULL || (at = ln->data)->attr != ATTR_PASSWORD) { + if (nc->code == CODE_WRITE_REQ) + nc->error = ERROR_DENIED; + goto end; + } + + if (at->size == 0) { + nc->error = ERROR_DENIED; + goto end; + } + + /* normally, we would expect password encryption to be handled + * in read requests as well as in write requests + * but it seems that real Netgear switches that support + * password encryption do NOT accept encrypted passwords in + * read requests + * this seems more to be a bug in their firmwares, however, as + * the scope of this program is to simulate a switch, we adopt + * the same buggy behaviour + */ + if (nc->code == CODE_WRITE_REQ && swi.encpass) + passwordEndecode(at->data, at->size); + text = at->data; + text[at->size] = '\0'; + trim(text, at->size); + if (strcmp(text, swi.password) != 0) + nc->error = ERROR_DENIED; + + +end: + if (nc->error == 0) { + /* correct password */ + if (ln != NULL && at->attr == ATTR_PASSWORD) + destroyElement(attr, ln, (void(*)(void*))freeAttr); + return 0; + } else { + /* invalid password, empty list */ + nc->attr_error = ATTR_PASSWORD; + clearList(attr, (void(*)(void*))freeAttr); + return -EACCES; + } +} + + +static void process_packet (struct nsdp_cmd *nc, List *attr) +{ + struct ListNode *ln, *pr; + struct attr *at; + int err; + int (*process_attr)(struct nsdp_cmd *, List *, struct attr *); + + + check_password(nc, attr); + if (nc->code == CODE_READ_REQ) { + nc->code = CODE_READ_REP; + process_attr = process_read_attr; + } else if (nc->code == CODE_WRITE_REQ) { + nc->code = CODE_WRITE_REP; + process_attr = process_write_attr; + } else { + /* invalid operation code */ + return; + } + + + for (ln = attr->first; ln != NULL; ) { + at = ln->data; + + err = process_attr(nc, attr, at); + + if (err == 1) { + /* destroy current attribute */ + pr = ln; + ln = ln->next; + destroyElement(attr, pr, (void(*)(void*))freeAttr); + } else if (err == 0) { + /* keep current attribute */ + ln = ln->next; + } else { + /* error, abort */ + return; + } + } + + memcpy(&nc->switch_mac, swi.mac, ETH_ALEN); + nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + + sendNsdpPacket(sock, nc, attr); +} + + +static int netdev_info (const char *dev) +{ + struct ifreq ifr; + int err; + + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1); + + /* get interface MAC */ + err = ioctl(sock, SIOCGIFHWADDR, &ifr, sizeof(struct ifreq)); + if (err < 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return err; + } + memcpy(swi.mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* get interface IP */ + err = ioctl(sock, SIOCGIFADDR, &ifr, sizeof(struct ifreq)); + if (err < 0) { + perror("ioctl(SIOCGIFADDR)"); + return err; + } + swi.ip = (*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr.s_addr; + + /* get interface netmask */ + err = ioctl(sock, SIOCGIFNETMASK, &ifr, sizeof(struct ifreq)); + if (err < 0) { + perror("ioctl(SIOCGIFNETMASK)"); + return err; + } + swi.netmask = (*(struct sockaddr_in*)&ifr.ifr_netmask).sin_addr.s_addr; + + /* TODO: get netmask */ + swi.gw = htonl(ntohl(swi.ip & swi.netmask) | 1); + + + return 0; +} + + +static void handler (int sig) +{ + (void)sig; + printf("interrupt\n"); +} + + +int main (int argc, char **argv) +{ + static const struct option options[] = { + {"help", no_argument, NULL, 'h'}, + {"interface", required_argument, NULL, 'i'}, + {0, 0, 0, 0} + }; + int err = 0, len; + const char *iface = "eth0"; + struct sockaddr_in local; + struct sigaction sa; + struct nsdp_cmd nc; + List *attr; + + + opterr = 0; + + while ((len = getopt_long(argc, argv, "hi:", options, NULL)) != -1) { + switch (len) { + + case 'h': + printf("usage: %s [-h] [-i ]\n", argv[0]); + goto end; + + case 'i': + iface = optarg; + break; + + case '?': + printf("unknown option: \"%s\"\n", argv[optind - 1]); + goto end; + } + } + + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + perror("socket"); + err = 1; + goto end; + } + + memset(&local, 0, sizeof(struct sockaddr_in)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(SWITCH_PORT); + + if (bind(sock, (struct sockaddr*)&local, sizeof(struct sockaddr_in)) < 0) { + perror("bind"); + err = 1; + goto end; + } + + /* allow broadcasting */ + len = 1; + len = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &len, sizeof(len)); + if (len < 0) { + perror("setsockopt(SO_BROADCAST)"); + err = 1; + goto end; + } + + /* initialize switch infos */ + memset(&swi, 0, sizeof(struct swi_info)); + strncpy(swi.product, "NgEmu_0.1", MAX_STR_SIZE); + strncpy(swi.name, "Netgear Switch Emulator", MAX_STR_SIZE); + strncpy(swi.firmware, "0.1", MAX_STR_SIZE); + strncpy(swi.password, "password", MAX_STR_SIZE); + swi.encpass = true; + swi.ports = 8; + swi.dhcp = false; + if (netdev_info(iface) < 0) + goto end; + + + attr = createEmptyList(); + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = handler; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + while (1) { + memset(&nc, 0, sizeof(struct nsdp_cmd)); + nc.remote_addr.sin_family = AF_INET; + nc.remote_addr.sin_port = htons(CLIENT_PORT); + + len = recvNsdpPacket(sock, &nc, attr, NULL); + if (len < 0) + break; + + /* ignore packets not for us */ + for (len = 0; nc.switch_mac.ether_addr_octet[len] == 0 && len < ETH_ALEN; len++); + if (len < ETH_ALEN && memcmp(swi.mac, &nc.switch_mac, ETH_ALEN) != 0) + continue; + + process_packet(&nc, attr); + clearList(attr, (void(*)(void*))freeAttr); + } + + destroyList(attr, (void(*)(void*))freeAttr); + +end: + close(sock); + + return err; +} + +