X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=lib%2Fsrc%2Fnetwork.c;h=f53b4756d5914a8c05706ffd2eef1cd948ff6db5;hb=c46b619e7fb06c397d12a446b9146dc80ffca6e9;hp=d548b58edccc6c7f2dffbe405caa3451e790977c;hpb=25c375ef21fa0c0d46b6d7a45acd4d613f1fe907;p=ngadmin diff --git a/lib/src/network.c b/lib/src/network.c index d548b58..f53b475 100644 --- a/lib/src/network.c +++ b/lib/src/network.c @@ -1,344 +1,399 @@ -#include "network.h" - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -// ----------------------------------- -int startNetwork (struct ngadmin *nga) { - - struct ifreq ifr; - int ret; - - - memset(&nga->local, 0, sizeof(struct sockaddr_in)); - nga->local.sin_family=AF_INET; - nga->local.sin_port=htons(CLIENT_PORT); - - memset(&ifr, 0, sizeof(struct ifreq)); - strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ-1); - - if ( (nga->sock=socket(AF_INET, SOCK_DGRAM, 0))<0 ) { - perror("socket"); - return nga->sock; - } - - /* - // get the interface IP address - if ( (ret=ioctl(nga->sock, SIOCGIFADDR, &ifr))<0 ) { - perror("ioctl(SIOCGIFADDR)"); - close(nga->sock); - return ret; - } - */ - /* - Here we have a problem: when you have multiple interfaces, sending a packet to - 255.255.255.255 may not send it to the interface you want. If you bind() to - the address of the interface, you will not be able to receive broadcasts. - The only solution I have found yet is in this case to use - setsockopt(SO_BINDTODEVICE) but this requires root priviledges. - */ - //local.sin_addr=(*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr; // FIXME - - // get the interface broadcast address - if ( (ret=ioctl(nga->sock, SIOCGIFBRDADDR, &ifr))<0 ) { - perror("ioctl(SIOCGIFBRDADDR)"); - close(nga->sock); - return ret; - } - nga->brd=(*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr; - - // get the interface MAC address - if ( (ret=ioctl(nga->sock, SIOCGIFHWADDR, &ifr))<0 ) { - perror("ioctl(SIOCGIFHWADDR)"); - close(nga->sock); - return ret; - } - memcpy(&nga->localmac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - // bind - if ( (ret=bind(nga->sock, (struct sockaddr*)&nga->local, sizeof(struct sockaddr_in)))<0 ) { - perror("bind"); - close(nga->sock); - return ret; - } - - // allow broadcasting - ret=1; - if ( (ret=setsockopt(nga->sock, SOL_SOCKET, SO_BROADCAST, &ret, sizeof(ret)))<0 ) { - perror("setsockopt(SO_BROADCAST)"); - return ret; - } - - // prevent unicast packets from being routed - ret=1; - if ( (ret=setsockopt(nga->sock, IPPROTO_IP, IP_TTL, &ret, sizeof(ret)))<0 ) { - perror("setsockopt(IP_TTL)"); - return ret; - } - - - return 0; - -} +#include "network.h" +int startNetwork (struct ngadmin *nga) +{ + struct ifreq ifr; + int ret; + + + /* create socket */ + nga->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (nga->sock < 0) { + perror("socket"); + return nga->sock; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1); -// ---------------------------------- -int stopNetwork (struct ngadmin *nga) { - - return close(nga->sock); - + /* get the interface MAC address */ + ret = ioctl(nga->sock, SIOCGIFHWADDR, &ifr); + if (ret < 0) { + perror("ioctl(SIOCGIFHWADDR)"); + close(nga->sock); + return ret; + } + memcpy(&nga->localmac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* bind */ + memset(&nga->local, 0, sizeof(struct sockaddr_in)); + nga->local.sin_family = AF_INET; + nga->local.sin_port = htons(CLIENT_PORT); + + ret = bind(nga->sock, (struct sockaddr*)&nga->local, sizeof(struct sockaddr_in)); + if (ret < 0) { + perror("bind"); + close(nga->sock); + return ret; + } + + /* allow broadcasting */ + ret = 1; + ret = setsockopt(nga->sock, SOL_SOCKET, SO_BROADCAST, &ret, sizeof(ret)); + if (ret < 0) { + perror("setsockopt(SO_BROADCAST)"); + return ret; + } + + /* prevent unicast packets from being routed by setting the TTL to 1 */ + ret = 1; + ret = setsockopt(nga->sock, IPPROTO_IP, IP_TTL, &ret, sizeof(ret)); + if (ret < 0) { + perror("setsockopt(IP_TTL)"); + return ret; + } + + + return 0; } - -// ------------------------------------- -int forceInterface (struct ngadmin *nga) { - - int ret; - - - /* - As described bellow, when you have multiple interfaces, this forces the packet - to go to a particular interface. - */ - if ( (ret=setsockopt(nga->sock, SOL_SOCKET, SO_BINDTODEVICE, nga->iface, strlen(nga->iface)+1))<0 ) { - perror("setsockopt(SO_BINDTODEVICE)"); - return ret; - } - - /* - If the switch's IP is not in your network range, for instance because you do - not have DHCP enabled or you started the switch after it, this allows to - bypass the routing tables and consider every address is directly reachable on - the interface. - */ - ret=1; - if ( (ret=setsockopt(nga->sock, SOL_SOCKET, SO_DONTROUTE, &ret, sizeof(ret)))<0 ) { - perror("setsockopt(SO_DONTROUTE)"); - return ret; - } - - - return 0; - +int setBroadcastType (struct ngadmin *nga, bool value) +{ + int ret; + struct ifreq ifr; + + + nga->globalbroad = value; + if (value) { + nga->brd.s_addr = (in_addr_t)0; + return 0; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1); + + /* get the interface broadcast address */ + ret = ioctl(nga->sock, SIOCGIFBRDADDR, &ifr); + if (ret < 0) { + perror("ioctl(SIOCGIFBRDADDR)"); + nga->brd.s_addr = (in_addr_t)0; + nga->globalbroad = true; + return ret; + } + + nga->brd = (*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr; + + return 0; } - -// ------------------------------------ -int updateTimeout (struct ngadmin *nga) { - - int ret; - - - // specify receive timeout - if ( (ret=setsockopt(nga->sock, SOL_SOCKET, SO_RCVTIMEO, &nga->timeout, sizeof(struct timeval)))<0 ) { - perror("setsockopt(SO_RCVTIMEO)"); - return ret; - } - - - return 0; - +int stopNetwork (struct ngadmin *nga) +{ + return close(nga->sock); } - -// ---------------------------------------------------------------- -int sendNgPacket (struct ngadmin *nga, char code, const List *attr) { - - char buffer[1500]; - struct ng_packet np; - ListNode *ln; - struct attr *at; - struct sockaddr_in remote; - const struct swi_attr *sa=nga->current; - int ret; - - - - - np.buffer=buffer; - np.maxlen=sizeof(buffer); - initNgPacket(&np); - initNgHeader(np.nh, code, &nga->localmac, sa==NULL ? NULL : &sa->mac , ++nga->seq); - - if ( attr!=NULL ) { - for (ln=attr->first; ln!=NULL; ln=ln->next) { - at=ln->data; - addPacketAttr(&np, at->attr, at->size, at->data); - } - } - - memset(&remote, 0, sizeof(struct sockaddr_in)); - remote.sin_family=AF_INET; - remote.sin_port=htons(SWITCH_PORT); - - if ( sa!=NULL && !nga->keepbroad ) remote.sin_addr=sa->nc.ip; - else if ( nga->globalbroad ) remote.sin_addr.s_addr=htonl(INADDR_BROADCAST); - else remote.sin_addr=nga->brd; - - - if ( (ret=sendto(nga->sock, buffer, getPacketTotalSize(&np), 0, (struct sockaddr*)&remote, sizeof(struct sockaddr_in)))<0 ) { - perror("sendto"); - } - - - return ret; - +int forceInterface (struct ngadmin *nga) +{ + int ret; + + + /* as described bellow, when you have multiple interfaces, this + * forces the packet to go to a particular interface + */ + ret = setsockopt(nga->sock, SOL_SOCKET, SO_BINDTODEVICE, nga->iface, strlen(nga->iface) + 1); + if (ret < 0) { + perror("setsockopt(SO_BINDTODEVICE)"); + return ret; + } + + /* if the switch's IP is not in your network range, for instance + * because you do not have DHCP enabled or you started the switch + * after your DHCP server, this allows to bypass the routing tables + * and consider every address is directly reachable on the interface + */ + ret = 1; + ret = setsockopt(nga->sock, SOL_SOCKET, SO_DONTROUTE, &ret, sizeof(ret)); + if (ret <0) { + perror("setsockopt(SO_DONTROUTE)"); + return ret; + } + + + return 0; } +static int checkErrorCode (const struct nsdp_cmd *nc) +{ + switch (nc->error) { + + case 0: + return ERR_OK; + + case ERROR_READONLY: + case ERROR_WRITEONLY: + return ERR_INVOP; -// ------------------------------------------------------------------------------------------------------------ -int recvNgPacket (struct ngadmin *nga, char code, unsigned char *error, unsigned short *attr_error, List *attr) { - - char buffer[1500]; - struct ng_packet np; - struct sockaddr_in remote; - socklen_t slen=sizeof(struct sockaddr_in); - const struct swi_attr *sa=nga->current; - int len; - - - np.buffer=buffer; - np.maxlen=sizeof(buffer); - - memset(&remote, 0, sizeof(struct sockaddr_in)); - remote.sin_family=AF_INET; - - while ( 1 ) { - - len=recvfrom(nga->sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&remote, &slen); - - if ( len<0 ) { - break; - } - - if ( ntohs(remote.sin_port)==SWITCH_PORT && len>=(int)sizeof(struct ng_header) && validateNgHeader(np.nh, code, &nga->localmac, sa==NULL ? NULL : &sa->mac , nga->seq) ) { - initNgPacket(&np); - extractPacketAttributes(&np, error, attr_error, attr); - len=0; - break; - } - - } - - - return len; - + case ERROR_DENIED: + return (nc->attr_error == ATTR_PASSWORD) ? ERR_BADPASS : ERR_DENIED; + + case ERROR_INVALID_VALUE: + return ERR_INVARG; + + default: + return ERR_UNKNOWN; + } } - -int checkErrorCode (unsigned char err, unsigned short attr_error) { - - - if ( err==ERROR_INVALID_PASSWORD && attr_error==ATTR_PASSWORD ) { - return ERR_BADPASS; - } - - if ( err==ERROR_INVALID_VALUE ) { - return ERR_INVARG; - } - - - return ERR_OK; - +void prepareSend (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code) +{ + struct swi_attr *sa = nga->current; + + + memset(nc, 0, sizeof(struct nsdp_cmd)); + memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN); + nc->remote_addr.sin_family = AF_INET; + nc->remote_addr.sin_port = htons(SWITCH_PORT); + if (sa != NULL) + memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN); + + /* destination address selection */ + if (sa != NULL && !nga->keepbroad) + nc->remote_addr.sin_addr = sa->nc.ip; + else if (nga->globalbroad) + nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + else + nc->remote_addr.sin_addr = nga->brd; + + nc->seqnum = ++nga->seq; + nc->code = code; } +void prepareRecv (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code) +{ + struct swi_attr *sa = nga->current; + + + memset(nc, 0, sizeof(struct nsdp_cmd)); + memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN); + nc->remote_addr.sin_family = AF_INET; + nc->remote_addr.sin_port = htons(SWITCH_PORT); + if (sa != NULL) + memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN); + + /* set filter on switch IP */ + if (sa == NULL) + nc->remote_addr.sin_addr.s_addr = htonl(INADDR_ANY); + else + nc->remote_addr.sin_addr = sa->nc.ip; + + nc->seqnum = nga->seq; + nc->code = code; +} + -// ---------------------------------------------- -int readRequest (struct ngadmin *nga, List *attr) { - - int i, ret=ERR_OK; - unsigned char err; - unsigned short attr_error; - - - if ( nga==NULL ) { - ret=ERR_INVARG; - goto end; - } - - // add end attribute to end - pushBackList(attr, newEmptyAttr(ATTR_END)); - - i=sendNgPacket(nga, CODE_READ_REQ, attr); - - // the list will be filled again by recvNgPacket - clearList(attr, (void(*)(void*))freeAttr); - - if ( i<0 || (i=recvNgPacket(nga, CODE_READ_REP, &err, &attr_error, attr))<0 ) { - ret= ( errno==EAGAIN || errno==EWOULDBLOCK ) ? ERR_TIMEOUT : ERR_NET ; - goto end; - } - - - // check error code - ret=checkErrorCode(err, attr_error); - - - end: - - return ret; - +int readRequest (struct ngadmin *nga, List *attr) +{ + int i, ret = ERR_OK; + struct nsdp_cmd nc; + + + if (nga == NULL) { + ret = ERR_INVARG; + goto end; + } + + /* add end attribute to end */ + pushBackList(attr, newEmptyAttr(ATTR_END)); + + prepareSend(nga, &nc, CODE_READ_REQ); + i = sendNsdpPacket(nga->sock, &nc, attr); + + /* do not destroy the list, it will be filled again later by recvNsdpPacket */ + clearList(attr, (void(*)(void*))freeAttr); + + if (i >= 0) { + prepareRecv(nga, &nc, CODE_READ_REP); + i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout); + } + + if (i == -EINVAL) { + ret = ERR_INVARG; + goto end; + } else if (i == -ETIMEDOUT) { + ret = ERR_TIMEOUT; + goto end; + } else if (i < 0) { + ret = ERR_NET; + goto end; + } + + /* check the switch error code */ + ret = checkErrorCode(&nc); + + +end: + return ret; } +int writeRequest (struct ngadmin *nga, List *attr) +{ + int i, ret = ERR_OK; + struct attr *at; + struct nsdp_cmd nc; + + + if (nga == NULL) { + ret = ERR_INVARG; + goto end; + } else if (nga->current == NULL) { + ret = ERR_NOTLOG; + goto end; + } + + + if (attr == NULL) + attr = createEmptyList(); + + /* add password attribute to start */ + at = newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)); + if (nga->encrypt_pass) + passwordEndecode(at->data, at->size); + pushFrontList(attr, at); + + /* add end attribute to end */ + pushBackList(attr, newEmptyAttr(ATTR_END)); + + prepareSend(nga, &nc, CODE_WRITE_REQ); + i = sendNsdpPacket(nga->sock, &nc, attr); + + /* the list will be filled again by recvNgPacket + * but normally it will be still empty + */ + clearList(attr, (void(*)(void*))freeAttr); + + if (i >= 0) { + prepareRecv(nga, &nc, CODE_WRITE_REP); + i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout); + } + + if (i == -EINVAL) { + ret = ERR_INVARG; + goto end; + } else if (i == -ETIMEDOUT) { + ret = ERR_TIMEOUT; + } else if (i < 0) { + ret = ERR_NET; + goto end; + } + + /* check the switch error code */ + ret = checkErrorCode(&nc); + + +end: + /* the switch replies to write request by just a header (no attributes), so the list can be destroyed */ + destroyList(attr, (void(*)(void*))freeAttr); + + + return ret; +} + -// ----------------------------------------------- -int writeRequest (struct ngadmin *nga, List *attr) { - - int i, ret=ERR_OK; - unsigned char err; - unsigned short attr_error; - - - if ( nga==NULL ) { - ret=ERR_INVARG; - goto end; - } else if ( nga->current==NULL ) { - ret=ERR_NOTLOG; - goto end; - } - - - if ( attr==NULL ) { - attr=createEmptyList(); - } - - // add password attribute to start - pushFrontList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password))); - - // add end attribute to end - pushBackList(attr, newEmptyAttr(ATTR_END)); - - i=sendNgPacket(nga, CODE_WRITE_REQ, attr); - - // the list will be filled again by recvNgPacket - // but normally it will be still empty - clearList(attr, (void(*)(void*))freeAttr); - - if ( i<0 || (i=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error, attr))<0 ) { - ret= ( errno==EAGAIN || errno==EWOULDBLOCK ) ? ERR_TIMEOUT : ERR_NET ; - goto end; - } - - // check error code - ret=checkErrorCode(err, attr_error); - - - end: - // the switch replies to write request by just a header (no attributes), so the list can be destroyed - destroyList(attr, (void(*)(void*))freeAttr); - - - return ret; - +int extractSwitchAttributes (struct swi_attr *sa, const List *l) +{ + const ListNode *ln; + const struct attr *at; + int len; + + + memset(sa, 0, sizeof(struct swi_attr)); + + for (ln = l->first; ln != NULL; ln = ln->next) { + at = ln->data; + if (at->size == 0) { + if (at->attr == ATTR_NAME) + continue; + else + return -EMSGSIZE; + } + + switch (at->attr) { + + case ATTR_PRODUCT: + len = min(at->size, PRODUCT_SIZE); + memcpy(sa->product, at->data, len); + trim(sa->product, len); + break; + + case ATTR_NAME: + len = min(at->size, NAME_SIZE); + memcpy(sa->name, at->data, len); + trim(sa->name, len); + break; + + case ATTR_MAC: + memcpy(&sa->mac, at->data, ETH_ALEN); + break; + + case ATTR_IP: + sa->nc.ip = *(struct in_addr*)at->data; + break; + + case ATTR_NETMASK: + sa->nc.netmask = *(struct in_addr*)at->data; + break; + + case ATTR_GATEWAY: + sa->nc.gw = *(struct in_addr*)at->data; + 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 + */ + sa->nc.dhcp = (at->size == 2) && ((*(unsigned short*)at->data) == 1); + break; + + case ATTR_FIRM_VER: + len = min(at->size, FIRMWARE_SIZE - 1); + memcpy(sa->firmware, at->data, len); + sa->firmware[len] = '\0'; + break; + + case ATTR_PORTS_COUNT: + sa->ports = *(unsigned char*)at->data; + break; + + case ATTR_END: + return 0; + } + } + + + return 0; }