]> git.sur5r.net Git - ngadmin/blob - lib/src/network.c
lib: fix bug on dhcp setting when switch sends a 1 byte reply
[ngadmin] / lib / src / network.c
1
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <arpa/inet.h>
7
8 #include <net/if.h>
9 #include <netinet/ether.h>
10 #include <sys/ioctl.h>
11
12 #include <nsdp/attr.h>
13 #include <nsdp/str.h>
14 #include <nsdp/net.h>
15 #include <nsdp/protocol.h>
16
17 #include "network.h"
18
19
20 int startNetwork (struct ngadmin *nga)
21 {
22         struct ifreq ifr;
23         int ret;
24         
25         
26         /* create socket */
27         nga->sock = socket(AF_INET, SOCK_DGRAM, 0);
28         if (nga->sock < 0) {
29                 perror("socket");
30                 return nga->sock;
31         }
32         
33         memset(&ifr, 0, sizeof(struct ifreq));
34         strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1);
35
36         /* get the interface MAC address */
37         ret = ioctl(nga->sock, SIOCGIFHWADDR, &ifr);
38         if (ret < 0) {
39                 perror("ioctl(SIOCGIFHWADDR)");
40                 close(nga->sock);
41                 return ret;
42         }
43         memcpy(&nga->localmac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
44         
45         /* bind */
46         memset(&nga->local, 0, sizeof(struct sockaddr_in));
47         nga->local.sin_family = AF_INET;
48         nga->local.sin_port = htons(CLIENT_PORT);
49         
50         ret = bind(nga->sock, (struct sockaddr*)&nga->local, sizeof(struct sockaddr_in));
51         if (ret < 0) {
52                 perror("bind");
53                 close(nga->sock);
54                 return ret;
55         }
56         
57         /* allow broadcasting */
58         ret = 1;
59         ret = setsockopt(nga->sock, SOL_SOCKET, SO_BROADCAST, &ret, sizeof(ret));
60         if (ret < 0) {
61                 perror("setsockopt(SO_BROADCAST)");
62                 return ret;
63         }
64         
65         /* prevent unicast packets from being routed by setting the TTL to 1 */
66         ret = 1;
67         ret = setsockopt(nga->sock, IPPROTO_IP, IP_TTL, &ret, sizeof(ret));
68         if (ret < 0) {
69                 perror("setsockopt(IP_TTL)");
70                 return ret;
71         }
72         
73         
74         return 0;
75 }
76
77
78 int setBroadcastType (struct ngadmin *nga, bool value)
79 {
80         int ret;
81         struct ifreq ifr;
82         
83         
84         nga->globalbroad = value;
85         if (value) {
86                 nga->brd.s_addr = (in_addr_t)0;
87                 return 0;
88         }
89         
90         memset(&ifr, 0, sizeof(struct ifreq));
91         strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1);
92         
93         /* get the interface broadcast address */
94         ret = ioctl(nga->sock, SIOCGIFBRDADDR, &ifr);
95         if (ret < 0) {
96                 perror("ioctl(SIOCGIFBRDADDR)");
97                 nga->brd.s_addr = (in_addr_t)0;
98                 nga->globalbroad = true;
99                 return ret;
100         }
101         
102         nga->brd = (*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr;
103         
104         return 0;
105 }
106
107
108 int stopNetwork (struct ngadmin *nga)
109 {
110         return close(nga->sock);
111 }
112
113
114 int forceInterface (struct ngadmin *nga)
115 {
116         int ret;
117         
118         
119         /* as described bellow, when you have multiple interfaces, this
120          * forces the packet to go to a particular interface
121          */
122         ret = setsockopt(nga->sock, SOL_SOCKET, SO_BINDTODEVICE, nga->iface, strlen(nga->iface) + 1);
123         if (ret < 0) {
124                 perror("setsockopt(SO_BINDTODEVICE)");
125                 return ret;
126         }
127         
128         /* if the switch's IP is not in your network range, for instance
129          * because you do not have DHCP  enabled or you started the switch
130          * after your DHCP server, this allows to bypass the routing tables
131          * and consider every address is directly reachable on the interface
132          */
133         ret = 1;
134         ret = setsockopt(nga->sock, SOL_SOCKET, SO_DONTROUTE, &ret, sizeof(ret));
135         if (ret <0) {
136                 perror("setsockopt(SO_DONTROUTE)");
137                 return ret;
138         }
139         
140         
141         return 0;
142 }
143
144
145 static int checkErrorCode (const struct nsdp_cmd *nc)
146 {
147         switch (nc->error) {
148         
149         case 0:
150                 return ERR_OK;
151         
152         case ERROR_READONLY:
153         case ERROR_WRITEONLY:
154                 return ERR_INVOP;
155
156         case ERROR_DENIED:
157                 return (nc->attr_error == ATTR_PASSWORD) ? ERR_BADPASS : ERR_DENIED;
158         
159         case ERROR_INVALID_VALUE:
160                 return ERR_INVARG;
161         
162         default:
163                 return ERR_UNKNOWN;
164         }
165 }
166
167
168 void prepareSend (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
169 {
170         struct swi_attr *sa = nga->current;
171         
172         
173         memset(nc, 0, sizeof(struct nsdp_cmd));
174         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
175         nc->remote_addr.sin_family = AF_INET;
176         nc->remote_addr.sin_port = htons(SWITCH_PORT);
177         if (sa != NULL)
178                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
179         
180         /* destination address selection */
181         if (sa != NULL && !nga->keepbroad)
182                 nc->remote_addr.sin_addr = sa->nc.ip;
183         else if (nga->globalbroad)
184                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
185         else
186                 nc->remote_addr.sin_addr = nga->brd;
187         
188         nc->seqnum = ++nga->seq;
189         nc->code = code;
190 }
191
192
193 void prepareRecv (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
194 {
195         struct swi_attr *sa = nga->current;
196         
197         
198         memset(nc, 0, sizeof(struct nsdp_cmd));
199         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
200         nc->remote_addr.sin_family = AF_INET;
201         nc->remote_addr.sin_port = htons(SWITCH_PORT);
202         if (sa != NULL)
203                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
204         
205         /* set filter on switch IP */
206         if (sa == NULL)
207                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_ANY);
208         else
209                 nc->remote_addr.sin_addr = sa->nc.ip;
210         
211         nc->seqnum = nga->seq;
212         nc->code = code;
213 }
214
215
216 int readRequest (struct ngadmin *nga, List *attr)
217 {
218         int i, ret = ERR_OK;
219         struct nsdp_cmd nc;
220         
221         
222         if (nga == NULL) {
223                 ret = ERR_INVARG;
224                 goto end;
225         }
226         
227         /* add end attribute to end */
228         pushBackList(attr, newEmptyAttr(ATTR_END));
229         
230         prepareSend(nga, &nc, CODE_READ_REQ);
231         i = sendNsdpPacket(nga->sock, &nc, attr);
232         
233         /* do not destroy the list, it will be filled again later by recvNsdpPacket */
234         clearList(attr, (void(*)(void*))freeAttr);
235         
236         if (i >= 0) {
237                 prepareRecv(nga, &nc, CODE_READ_REP);
238                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
239         }
240         
241         if (i == -EINVAL) {
242                 ret = ERR_INVARG;
243                 goto end;
244         } else if (i == -ETIMEDOUT) {
245                 ret = ERR_TIMEOUT;
246                 goto end;
247         } else if (i < 0) {
248                 ret = ERR_NET;
249                 goto end;
250         }
251         
252         /* check the switch error code */
253         ret = checkErrorCode(&nc);
254         
255         
256 end:
257         return ret;
258 }
259
260
261 int writeRequest (struct ngadmin *nga, List *attr)
262 {
263         int i, ret = ERR_OK;
264         struct attr *at;
265         struct nsdp_cmd nc;
266         
267         
268         if (nga == NULL) {
269                 ret = ERR_INVARG;
270                 goto end;
271         } else if (nga->current == NULL) {
272                 ret = ERR_NOTLOG;
273                 goto end;
274         }
275         
276         
277         if (attr == NULL)
278                 attr = createEmptyList();
279         
280         /* add password attribute to start */
281         at = newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password));
282         if (nga->encrypt_pass)
283                 passwordEndecode(at->data, at->size);
284         pushFrontList(attr, at);
285         
286         /* add end attribute to end */
287         pushBackList(attr, newEmptyAttr(ATTR_END));
288         
289         prepareSend(nga, &nc, CODE_WRITE_REQ);
290         i = sendNsdpPacket(nga->sock, &nc, attr);
291         
292         /* the list will be filled again by recvNgPacket
293          * but normally it will be still empty
294          */
295         clearList(attr, (void(*)(void*))freeAttr);
296         
297         if (i >= 0) {
298                 prepareRecv(nga, &nc, CODE_WRITE_REP);
299                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
300         }
301         
302         if (i == -EINVAL) {
303                 ret = ERR_INVARG;
304                 goto end;
305         } else if (i == -ETIMEDOUT) {
306                 ret = ERR_TIMEOUT;
307         } else if (i < 0) {
308                 ret = ERR_NET;
309                 goto end;
310         }
311         
312         /* check the switch error code */
313         ret = checkErrorCode(&nc);
314         
315         
316 end:
317         /* the switch replies to write request by just a header (no attributes), so the list can be destroyed */
318         destroyList(attr, (void(*)(void*))freeAttr);
319         
320         
321         return ret;
322 }
323
324
325 int extractSwitchAttributes (struct swi_attr *sa, const List *l)
326 {
327         const ListNode *ln;
328         const struct attr *at;
329         int len;
330         
331         
332         memset(sa, 0, sizeof(struct swi_attr));
333         
334         for (ln = l->first; ln != NULL; ln = ln->next) {
335                 at = ln->data;
336                 if (at->size == 0) {
337                         if (at->attr == ATTR_NAME)
338                                 continue;
339                         else
340                                 return -EMSGSIZE;
341                 }
342                 
343                 switch (at->attr) {
344                 
345                 case ATTR_PRODUCT:
346                         len = min(at->size, PRODUCT_SIZE);
347                         memcpy(sa->product, at->data, len);
348                         trim(sa->product, len);
349                         break;
350                 
351                 case ATTR_NAME:
352                         len = min(at->size, NAME_SIZE);
353                         memcpy(sa->name, at->data, len);
354                         trim(sa->name, len);
355                         break;
356                 
357                 case ATTR_MAC:
358                         memcpy(&sa->mac, at->data, ETH_ALEN);
359                         break;
360                 
361                 case ATTR_IP:
362                         sa->nc.ip = *(struct in_addr*)at->data;
363                         break;
364                 
365                 case ATTR_NETMASK:
366                         sa->nc.netmask = *(struct in_addr*)at->data;
367                         break;
368                 
369                 case ATTR_GATEWAY:
370                         sa->nc.gw = *(struct in_addr*)at->data;
371                         break;
372                 
373                 case ATTR_DHCP:
374                         /* Note: DHCP attribute is special, on read request some
375                          * switches send a 2 two bytes attribute and others a
376                          * 1 byte attribute, while all seem to accept a 1 byte
377                          * attribute on write request
378                          */
379                         if (at->size == 1)
380                                 sa->nc.dhcp = ((*(unsigned char*)at->data) == 1);
381                         else if (at->size == 2)
382                                 sa->nc.dhcp = ((*(unsigned short*)at->data) == 1);
383                         break;
384                 
385                 case ATTR_FIRM_VER:
386                         len = min(at->size, FIRMWARE_SIZE - 1);
387                         memcpy(sa->firmware, at->data, len);
388                         sa->firmware[len] = '\0';
389                         break;
390                 
391                 case ATTR_PORTS_COUNT:
392                         sa->ports = *(unsigned char*)at->data;
393                         break;
394                 
395                 case ATTR_END:
396                         return 0;
397                 }
398         }
399         
400         
401         return 0;
402 }
403
404