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