]> git.sur5r.net Git - ngadmin/blob - lib/src/network.c
Raw: separate attribute filtering in a separate function.
[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         
293         
294         if (nga == NULL) {
295                 ret = ERR_INVARG;
296                 goto end;
297         } else if (nga->current == NULL) {
298                 ret = ERR_NOTLOG;
299                 goto end;
300         }
301         
302         
303         if (attr == NULL)
304                 attr = createEmptyList();
305         
306         /* add password attribute to start */
307         pushFrontList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
308         
309         /* add end attribute to end */
310         pushBackList(attr, newEmptyAttr(ATTR_END));
311         
312         i = sendNgPacket(nga, CODE_WRITE_REQ, attr);
313         
314         /* the list will be filled again by recvNgPacket
315         but normally it will be still empty */
316         clearList(attr, (void(*)(void*))freeAttr);
317         
318         if (i >= 0)
319                 i = recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error, attr);
320         
321         if (i == -EINVAL) {
322                 ret = ERR_INVARG;
323                 goto end;
324         } else if (i < 0) {
325                 ret = ( errno==EAGAIN || errno==EWOULDBLOCK ) ? ERR_TIMEOUT : ERR_NET ;
326                 goto end;
327         }
328         
329         /* check the switch error code */
330         ret = checkErrorCode(err, attr_error);
331         
332         
333 end:
334         /* the switch replies to write request by just a header (no attributes), so the list can be destroyed */
335         destroyList(attr, (void(*)(void*))freeAttr);
336         
337         
338         return ret;
339 }
340
341
342 void extractSwitchAttributes (struct swi_attr *sa, const List *l)
343 {
344         const ListNode *ln;
345         const struct attr *at;
346         int len;
347         
348         
349         memset(sa, 0, sizeof(struct swi_attr));
350         
351         for (ln = l->first; ln != NULL; ln = ln->next) {
352                 at = ln->data;
353                 
354                 switch (at->attr) {
355                 
356                 case ATTR_PRODUCT:
357                         len = min(at->size, PRODUCT_SIZE);
358                         memcpy(sa->product, at->data, len);
359                         trim(sa->product, len);
360                         break;
361                 
362                 case ATTR_NAME:
363                         len = min(at->size, NAME_SIZE);
364                         memcpy(sa->name, at->data, len);
365                         trim(sa->name, len);
366                         break;
367                 
368                 case ATTR_MAC:
369                         memcpy(&sa->mac, at->data, ETH_ALEN);
370                         break;
371                 
372                 case ATTR_IP:
373                         sa->nc.ip = *(struct in_addr*)at->data;
374                         break;
375                 
376                 case ATTR_NETMASK:
377                         sa->nc.netmask = *(struct in_addr*)at->data;
378                         break;
379                 
380                 case ATTR_GATEWAY:
381                         sa->nc.gw = *(struct in_addr*)at->data;
382                         break;
383                 
384                 case ATTR_DHCP:
385                         sa->nc.dhcp = (ntohs(*(unsigned short*)at->data) == 1);
386                         break;
387                 
388                 case ATTR_FIRM_VER:
389                         len = min(at->size, FIRMWARE_SIZE - 1);
390                         memcpy(sa->firmware, at->data, len);
391                         sa->firmware[len] = '\0';
392                         break;
393                 
394                 case ATTR_PORTS_COUNT:
395                         sa->ports = *(unsigned char*)at->data;
396                         break;
397                 
398                 case ATTR_END:
399                         return;
400                 }
401         }
402 }
403
404