]> git.sur5r.net Git - ngadmin/blob - lib/src/network.c
Added the possibility to use global broadcast.
[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 // ------------------------------------------------------------------------------------------------------------
196 int recvNgPacket (struct ngadmin *nga, char code, unsigned char *error, unsigned short *attr_error, List *attr) {
197  
198  char buffer[1500];
199  struct ng_packet np;
200  struct sockaddr_in remote;
201  socklen_t slen=sizeof(struct sockaddr_in);
202  const struct swi_attr *sa=nga->current;
203  int len;
204  
205  
206  np.buffer=buffer;
207  np.maxlen=sizeof(buffer);
208  
209  memset(&remote, 0, sizeof(struct sockaddr_in));
210  remote.sin_family=AF_INET;
211  
212  while ( 1 ) {
213   
214   len=recvfrom(nga->sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&remote, &slen);
215   
216   if ( len<0 ) {
217    break;
218   }
219   
220   if ( ntohs(remote.sin_port)==SWITCH_PORT && len>=(int)sizeof(struct ng_header) && validateNgHeader(np.nh, code, &nga->localmac, sa==NULL ? NULL : &sa->mac , nga->seq) ) {
221    initNgPacket(&np);
222    extractPacketAttributes(&np, error, attr_error, attr);
223    len=0;
224    break;
225   }
226   
227  }
228  
229  
230  return len;
231  
232 }
233
234
235
236 int checkErrorCode (unsigned char err, unsigned short attr_error) {
237  
238  
239  if ( err==ERROR_INVALID_PASSWORD && attr_error==ATTR_PASSWORD ) {
240   return ERR_BADPASS;
241  }
242  
243  if ( err==ERROR_INVALID_VALUE ) {
244   return ERR_INVARG;
245  }
246  
247  
248  return ERR_OK;
249  
250 }
251
252
253
254 // ----------------------------------------------
255 int readRequest (struct ngadmin *nga, List *attr) {
256  
257  int i, ret=ERR_OK;
258  unsigned char err;
259  unsigned short attr_error;
260  
261  
262  if ( nga==NULL ) {
263   ret=ERR_INVARG;
264   goto end;
265  }
266  
267  // add end attribute to end
268  pushBackList(attr, newEmptyAttr(ATTR_END));
269  
270  i=sendNgPacket(nga, CODE_READ_REQ, attr);
271  
272  // the list will be filled again by recvNgPacket
273  clearList(attr, (void(*)(void*))freeAttr);
274  
275  if ( i<0 || (i=recvNgPacket(nga, CODE_READ_REP, &err, &attr_error, attr))<0 ) {
276   ret= ( errno==EAGAIN || errno==EWOULDBLOCK ) ? ERR_TIMEOUT : ERR_NET ;
277   goto end;
278  }
279  
280  
281  // check error code
282  ret=checkErrorCode(err, attr_error);
283  
284  
285  end:
286  
287  return ret;
288  
289 }
290
291
292
293 // -----------------------------------------------
294 int writeRequest (struct ngadmin *nga, List *attr) {
295  
296  int i, ret=ERR_OK;
297  unsigned char err;
298  unsigned short attr_error;
299  
300  
301  if ( nga==NULL ) {
302   ret=ERR_INVARG;
303   goto end;
304  } else if ( nga->current==NULL ) {
305   ret=ERR_NOTLOG;
306   goto end;
307  }
308  
309  
310  if ( attr==NULL ) {
311   attr=createEmptyList();
312  }
313  
314  // add password attribute to start
315  pushFrontList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
316  
317  // add end attribute to end
318  pushBackList(attr, newEmptyAttr(ATTR_END));
319  
320  i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
321  
322  // the list will be filled again by recvNgPacket
323  // but normally it will be still empty
324  clearList(attr, (void(*)(void*))freeAttr);
325  
326  if ( i<0 || (i=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error, attr))<0 ) {
327   ret= ( errno==EAGAIN || errno==EWOULDBLOCK ) ? ERR_TIMEOUT : ERR_NET ;
328   goto end;
329  }
330  
331  // check error code
332  ret=checkErrorCode(err, attr_error);
333  
334  
335  end:
336  // the switch replies to write request by just a header (no attributes), so the list can be destroyed
337  destroyList(attr, (void(*)(void*))freeAttr);
338  
339  
340  return ret;
341  
342 }
343
344