]> git.sur5r.net Git - ngadmin/blob - lib/src/network.c
09dcd6043142eea70b46c326cab5917c6a7d8891
[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 <nsdp/attr.h>
13 #include <nsdp/misc.h>
14 #include <nsdp/net.h>
15 #include <nsdp/protocol.h>
16
17 #include "network.h"
18
19
20 int startNetwork (struct ngadmin *nga)
21 {
22         struct ifreq ifr;
23         int ret;
24         
25         
26         /* create socket */
27         nga->sock = socket(AF_INET, SOCK_DGRAM, 0);
28         if (nga->sock < 0) {
29                 perror("socket");
30                 return nga->sock;
31         }
32         
33         memset(&ifr, 0, sizeof(struct ifreq));
34         strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1);
35
36         /* get the interface MAC address */
37         ret = ioctl(nga->sock, SIOCGIFHWADDR, &ifr);
38         if (ret < 0) {
39                 perror("ioctl(SIOCGIFHWADDR)");
40                 close(nga->sock);
41                 return ret;
42         }
43         memcpy(&nga->localmac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
44         
45         /* bind */
46         memset(&nga->local, 0, sizeof(struct sockaddr_in));
47         nga->local.sin_family = AF_INET;
48         nga->local.sin_port = htons(CLIENT_PORT);
49         
50         ret = bind(nga->sock, (struct sockaddr*)&nga->local, sizeof(struct sockaddr_in));
51         if (ret < 0) {
52                 perror("bind");
53                 close(nga->sock);
54                 return ret;
55         }
56         
57         /* allow broadcasting */
58         ret = 1;
59         ret = setsockopt(nga->sock, SOL_SOCKET, SO_BROADCAST, &ret, sizeof(ret));
60         if (ret < 0) {
61                 perror("setsockopt(SO_BROADCAST)");
62                 return ret;
63         }
64         
65         /* prevent unicast packets from being routed by setting the TTL to 1 */
66         ret = 1;
67         ret = setsockopt(nga->sock, IPPROTO_IP, IP_TTL, &ret, sizeof(ret));
68         if (ret < 0) {
69                 perror("setsockopt(IP_TTL)");
70                 return ret;
71         }
72         
73         
74         return 0;
75 }
76
77
78 int setBroadcastType (struct ngadmin *nga, bool value)
79 {
80         int ret;
81         struct ifreq ifr;
82         
83         
84         nga->globalbroad = value;
85         if (value) {
86                 nga->brd.s_addr = (in_addr_t)0;
87                 return 0;
88         }
89         
90         memset(&ifr, 0, sizeof(struct ifreq));
91         strncpy(ifr.ifr_name, nga->iface, IFNAMSIZ - 1);
92         
93         /* get the interface broadcast address */
94         ret = ioctl(nga->sock, SIOCGIFBRDADDR, &ifr);
95         if (ret < 0) {
96                 perror("ioctl(SIOCGIFBRDADDR)");
97                 nga->brd.s_addr = (in_addr_t)0;
98                 nga->globalbroad = true;
99                 return ret;
100         }
101         
102         nga->brd = (*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr;
103         
104         return 0;
105 }
106
107
108 int stopNetwork (struct ngadmin *nga)
109 {
110         return close(nga->sock);
111 }
112
113
114 int forceInterface (struct ngadmin *nga)
115 {
116         int ret;
117         
118         
119         /*
120         As described bellow, when you have multiple interfaces, this forces the packet 
121         to go to a particular interface. 
122         */
123         ret = setsockopt(nga->sock, SOL_SOCKET, SO_BINDTODEVICE, nga->iface, strlen(nga->iface) + 1);
124         if (ret < 0) {
125                 perror("setsockopt(SO_BINDTODEVICE)");
126                 return ret;
127         }
128         
129         /*
130         If the switch's IP is not in your network range, for instance because you do 
131         not have DHCP  enabled or you started the switch after it, this allows to 
132         bypass the routing tables and consider every address is directly reachable on 
133         the interface. 
134         */
135         ret = 1;
136         ret = setsockopt(nga->sock, SOL_SOCKET, SO_DONTROUTE, &ret, sizeof(ret));
137         if (ret <0) {
138                 perror("setsockopt(SO_DONTROUTE)");
139                 return ret;
140         }
141         
142         
143         return 0;
144 }
145
146
147 int updateTimeout (struct ngadmin *nga)
148 {
149         int ret;
150         
151         
152         /* specify receive timeout */
153         ret = setsockopt(nga->sock, SOL_SOCKET, SO_RCVTIMEO, &nga->timeout, sizeof(struct timeval));
154         if (ret < 0) {
155                 perror("setsockopt(SO_RCVTIMEO)");
156                 return ret;
157         }
158         
159         
160         return 0;
161 }
162
163
164 static int checkErrorCode (const struct nsdp_cmd *nc)
165 {
166         switch (nc->error) {
167         
168         case ERROR_DENIED:
169                 return (nc->attr_error == ATTR_PASSWORD) ? ERR_BADPASS : ERR_DENIED;
170         
171         case ERROR_INVALID_VALUE:
172                 return ERR_INVARG;
173         
174         default:
175                 return ERR_OK;
176         }
177 }
178
179
180 void prepareSend (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
181 {
182         struct swi_attr *sa = nga->current;
183         
184         
185         memset(nc, 0, sizeof(struct nsdp_cmd));
186         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
187         nc->remote_addr.sin_family = AF_INET;
188         nc->remote_addr.sin_port = htons(SWITCH_PORT);
189         if (sa != NULL) {
190                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
191                 nc->ports = sa->ports;
192         }
193         
194         /* destination address selection */
195         if (sa != NULL && !nga->keepbroad)
196                 nc->remote_addr.sin_addr = sa->nc.ip;
197         else if (nga->globalbroad)
198                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
199         else
200                 nc->remote_addr.sin_addr = nga->brd;
201         
202         nc->seqnum = ++nga->seq;
203         nc->code = code;
204 }
205
206
207 void prepareRecv (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
208 {
209         struct swi_attr *sa = nga->current;
210         
211         
212         memset(nc, 0, sizeof(struct nsdp_cmd));
213         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
214         nc->remote_addr.sin_family = AF_INET;
215         nc->remote_addr.sin_port = htons(SWITCH_PORT);
216         if (sa != NULL) {
217                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
218                 nc->ports = sa->ports;
219         }
220         
221         /* set filter on switch IP */
222         if (sa == NULL)
223                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_ANY);
224         else
225                 nc->remote_addr.sin_addr = sa->nc.ip;
226         
227         nc->seqnum = nga->seq;
228         nc->code = code;
229 }
230
231
232 int readRequest (struct ngadmin *nga, List *attr)
233 {
234         int i, ret = ERR_OK;
235         struct nsdp_cmd nc;
236         
237         
238         if (nga == NULL) {
239                 ret = ERR_INVARG;
240                 goto end;
241         }
242         
243         /* add end attribute to end */
244         pushBackList(attr, newEmptyAttr(ATTR_END));
245         
246         prepareSend(nga, &nc, CODE_READ_REQ);
247         i = sendNsdpPacket(nga->sock, &nc, attr);
248         
249         /* do not destroy the list, it will be filled again later by recvNsdpPacket */
250         clearList(attr, (void(*)(void*))freeAttr);
251         
252         if (i >= 0) {
253                 prepareRecv(nga, &nc, CODE_READ_REP);
254                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
255         }
256         
257         if (i == -EINVAL) {
258                 ret = ERR_INVARG;
259                 goto end;
260         } else if (i < 0) {
261                 ret = (errno == EAGAIN || errno == EWOULDBLOCK) ? ERR_TIMEOUT : ERR_NET;
262                 goto end;
263         }
264         
265         
266         /* check the switch error code */
267         ret = checkErrorCode(&nc);
268         
269         
270 end:
271         return ret;
272 }
273
274
275 int writeRequest (struct ngadmin *nga, List *attr)
276 {
277         int i, ret = ERR_OK;
278         struct attr *at;
279         struct nsdp_cmd nc;
280         
281         
282         if (nga == NULL) {
283                 ret = ERR_INVARG;
284                 goto end;
285         } else if (nga->current == NULL) {
286                 ret = ERR_NOTLOG;
287                 goto end;
288         }
289         
290         
291         if (attr == NULL)
292                 attr = createEmptyList();
293         
294         /* add password attribute to start */
295         at = newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password));
296         if (nga->encrypt_pass)
297                 passwordEndecode(at->data, at->size);
298         pushFrontList(attr, at);
299         
300         /* add end attribute to end */
301         pushBackList(attr, newEmptyAttr(ATTR_END));
302         
303         prepareSend(nga, &nc, CODE_WRITE_REQ);
304         i = sendNsdpPacket(nga->sock, &nc, attr);
305         
306         /* the list will be filled again by recvNgPacket
307         but normally it will be still empty */
308         clearList(attr, (void(*)(void*))freeAttr);
309         
310         if (i >= 0) {
311                 prepareRecv(nga, &nc, CODE_WRITE_REP);
312                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
313         }
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(&nc);
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