]> git.sur5r.net Git - ngadmin/blob - lib/src/network.c
0ad90561c9eb70443ff5357cf928793d03b9e674
[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 static int checkErrorCode (const struct nsdp_cmd *nc)
148 {
149         switch (nc->error) {
150         
151         case ERROR_DENIED:
152                 return (nc->attr_error == ATTR_PASSWORD) ? ERR_BADPASS : ERR_DENIED;
153         
154         case ERROR_INVALID_VALUE:
155                 return ERR_INVARG;
156         
157         default:
158                 return ERR_OK;
159         }
160 }
161
162
163 void prepareSend (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
164 {
165         struct swi_attr *sa = nga->current;
166         
167         
168         memset(nc, 0, sizeof(struct nsdp_cmd));
169         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
170         nc->remote_addr.sin_family = AF_INET;
171         nc->remote_addr.sin_port = htons(SWITCH_PORT);
172         if (sa != NULL) {
173                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
174                 nc->ports = sa->ports;
175         }
176         
177         /* destination address selection */
178         if (sa != NULL && !nga->keepbroad)
179                 nc->remote_addr.sin_addr = sa->nc.ip;
180         else if (nga->globalbroad)
181                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
182         else
183                 nc->remote_addr.sin_addr = nga->brd;
184         
185         nc->seqnum = ++nga->seq;
186         nc->code = code;
187 }
188
189
190 void prepareRecv (struct ngadmin *nga, struct nsdp_cmd *nc, unsigned char code)
191 {
192         struct swi_attr *sa = nga->current;
193         
194         
195         memset(nc, 0, sizeof(struct nsdp_cmd));
196         memcpy(&nc->client_mac, &nga->localmac, ETH_ALEN);
197         nc->remote_addr.sin_family = AF_INET;
198         nc->remote_addr.sin_port = htons(SWITCH_PORT);
199         if (sa != NULL) {
200                 memcpy(&nc->switch_mac, &sa->mac, ETH_ALEN);
201                 nc->ports = sa->ports;
202         }
203         
204         /* set filter on switch IP */
205         if (sa == NULL)
206                 nc->remote_addr.sin_addr.s_addr = htonl(INADDR_ANY);
207         else
208                 nc->remote_addr.sin_addr = sa->nc.ip;
209         
210         nc->seqnum = nga->seq;
211         nc->code = code;
212 }
213
214
215 int readRequest (struct ngadmin *nga, List *attr)
216 {
217         int i, ret = ERR_OK;
218         struct nsdp_cmd nc;
219         
220         
221         if (nga == NULL) {
222                 ret = ERR_INVARG;
223                 goto end;
224         }
225         
226         /* add end attribute to end */
227         pushBackList(attr, newEmptyAttr(ATTR_END));
228         
229         prepareSend(nga, &nc, CODE_READ_REQ);
230         i = sendNsdpPacket(nga->sock, &nc, attr);
231         
232         /* do not destroy the list, it will be filled again later by recvNsdpPacket */
233         clearList(attr, (void(*)(void*))freeAttr);
234         
235         if (i >= 0) {
236                 prepareRecv(nga, &nc, CODE_READ_REP);
237                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
238         }
239         
240         if (i == -EINVAL) {
241                 ret = ERR_INVARG;
242                 goto end;
243         } else if (i < 0) {
244                 ret = (errno == EAGAIN || errno == EWOULDBLOCK) ? ERR_TIMEOUT : ERR_NET;
245                 goto end;
246         }
247         
248         
249         /* check the switch error code */
250         ret = checkErrorCode(&nc);
251         
252         
253 end:
254         return ret;
255 }
256
257
258 int writeRequest (struct ngadmin *nga, List *attr)
259 {
260         int i, ret = ERR_OK;
261         struct attr *at;
262         struct nsdp_cmd nc;
263         
264         
265         if (nga == NULL) {
266                 ret = ERR_INVARG;
267                 goto end;
268         } else if (nga->current == NULL) {
269                 ret = ERR_NOTLOG;
270                 goto end;
271         }
272         
273         
274         if (attr == NULL)
275                 attr = createEmptyList();
276         
277         /* add password attribute to start */
278         at = newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password));
279         if (nga->encrypt_pass)
280                 passwordEndecode(at->data, at->size);
281         pushFrontList(attr, at);
282         
283         /* add end attribute to end */
284         pushBackList(attr, newEmptyAttr(ATTR_END));
285         
286         prepareSend(nga, &nc, CODE_WRITE_REQ);
287         i = sendNsdpPacket(nga->sock, &nc, attr);
288         
289         /* the list will be filled again by recvNgPacket
290         but normally it will be still empty */
291         clearList(attr, (void(*)(void*))freeAttr);
292         
293         if (i >= 0) {
294                 prepareRecv(nga, &nc, CODE_WRITE_REP);
295                 i = recvNsdpPacket(nga->sock, &nc, attr, &nga->timeout);
296         }
297         
298         if (i == -EINVAL) {
299                 ret = ERR_INVARG;
300                 goto end;
301         } else if (i < 0) {
302                 ret = (errno == EAGAIN || errno == EWOULDBLOCK) ? ERR_TIMEOUT : ERR_NET;
303                 goto end;
304         }
305         
306         /* check the switch error code */
307         ret = checkErrorCode(&nc);
308         
309         
310 end:
311         /* the switch replies to write request by just a header (no attributes), so the list can be destroyed */
312         destroyList(attr, (void(*)(void*))freeAttr);
313         
314         
315         return ret;
316 }
317
318
319 void extractSwitchAttributes (struct swi_attr *sa, const List *l)
320 {
321         const ListNode *ln;
322         const struct attr *at;
323         int len;
324         
325         
326         memset(sa, 0, sizeof(struct swi_attr));
327         
328         for (ln = l->first; ln != NULL; ln = ln->next) {
329                 at = ln->data;
330                 
331                 switch (at->attr) {
332                 
333                 case ATTR_PRODUCT:
334                         len = min(at->size, PRODUCT_SIZE);
335                         memcpy(sa->product, at->data, len);
336                         trim(sa->product, len);
337                         break;
338                 
339                 case ATTR_NAME:
340                         len = min(at->size, NAME_SIZE);
341                         memcpy(sa->name, at->data, len);
342                         trim(sa->name, len);
343                         break;
344                 
345                 case ATTR_MAC:
346                         memcpy(&sa->mac, at->data, ETH_ALEN);
347                         break;
348                 
349                 case ATTR_IP:
350                         sa->nc.ip = *(struct in_addr*)at->data;
351                         break;
352                 
353                 case ATTR_NETMASK:
354                         sa->nc.netmask = *(struct in_addr*)at->data;
355                         break;
356                 
357                 case ATTR_GATEWAY:
358                         sa->nc.gw = *(struct in_addr*)at->data;
359                         break;
360                 
361                 case ATTR_DHCP:
362                         sa->nc.dhcp = (ntohs(*(unsigned short*)at->data) == 1);
363                         break;
364                 
365                 case ATTR_FIRM_VER:
366                         len = min(at->size, FIRMWARE_SIZE - 1);
367                         memcpy(sa->firmware, at->data, len);
368                         sa->firmware[len] = '\0';
369                         break;
370                 
371                 case ATTR_PORTS_COUNT:
372                         sa->ports = *(unsigned char*)at->data;
373                         break;
374                 
375                 case ATTR_END:
376                         return;
377                 }
378         }
379 }
380
381