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