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