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