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