]> git.sur5r.net Git - ngadmin/blob - emu/src/emu.c
Add basic switch emulator
[ngadmin] / emu / src / emu.c
1
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <signal.h>
7 #include <arpa/inet.h>
8 #include <getopt.h>
9
10 #include <sys/ioctl.h>
11 #include <net/if.h>
12
13 #include <ngadmin.h> /* FIXME */
14 #include <nsdp/protocol.h>
15 #include <nsdp/attr.h>
16 #include <nsdp/net.h>
17 #include <nsdp/misc.h>
18
19
20 #define MAX_STR_SIZE    64
21
22 struct swi_info {
23         char product[MAX_STR_SIZE];
24         char name[MAX_STR_SIZE];
25         char firmware[MAX_STR_SIZE];
26         char password[MAX_STR_SIZE];
27         bool encpass;
28         unsigned char ports;
29         unsigned char mac[ETH_ALEN];
30         in_addr_t ip;
31         in_addr_t netmask;
32         in_addr_t gw;
33         bool dhcp;
34 };
35
36
37 static struct swi_info swi;
38 static int sock;
39
40
41 static int process_read_attr (struct nsdp_cmd *nc, List *attr, struct attr *at)
42 {
43         unsigned char p, *byte;
44         unsigned short *word;
45         unsigned int *dword;
46         struct attr_port_status *apu;
47         struct attr_port_stat *api;
48         
49         
50         switch (at->attr) {
51         }
52         
53         at->size = 0;
54         free(at->data);
55         at->data = NULL;
56         
57         switch (at->attr) {
58         
59         case ATTR_PRODUCT:
60                 byte = malloc(MAX_STR_SIZE);
61                 memcpy(byte, swi.product, MAX_STR_SIZE);
62                 at->data = byte;
63                 at->size = MAX_STR_SIZE;
64                 break;
65         
66         case ATTR_NAME:
67                 byte = malloc(MAX_STR_SIZE);
68                 memcpy(byte, swi.name, MAX_STR_SIZE);
69                 at->data = byte;
70                 at->size = MAX_STR_SIZE;
71                 break;
72         
73         case ATTR_FIRM_VER:
74                 byte = malloc(MAX_STR_SIZE);
75                 memcpy(byte, swi.firmware, MAX_STR_SIZE);
76                 at->data = byte;
77                 at->size = MAX_STR_SIZE;
78                 break;
79         
80         case ATTR_MAC:
81                 byte = malloc(ETH_ALEN);
82                 memcpy(byte, swi.mac, ETH_ALEN);
83                 at->data = byte;
84                 at->size = ETH_ALEN;
85                 break;
86         
87         case ATTR_PORTS_COUNT:
88                 byte = malloc(1);
89                 *byte = swi.ports;
90                 at->data = byte;
91                 at->size = 1;
92                 break;
93         
94         case ATTR_IP:
95                 byte = malloc(sizeof(in_addr_t));
96                 *(in_addr_t*)byte = swi.ip;
97                 at->data = byte;
98                 at->size = sizeof(in_addr_t);
99                 break;
100         
101         case ATTR_NETMASK:
102                 byte = malloc(sizeof(in_addr_t));
103                 *(in_addr_t*)byte = swi.netmask;
104                 at->data = byte;
105                 at->size = sizeof(in_addr_t);
106                 break;
107         
108         case ATTR_GATEWAY:
109                 byte = malloc(sizeof(in_addr_t));
110                 *(in_addr_t*)byte = swi.gw;
111                 at->data = byte;
112                 at->size = sizeof(in_addr_t);
113                 break;
114         
115         case ATTR_DHCP:
116                 /* Note: DHCP attribute is special, it is 2 two bytes long
117                  * when sent by the switch but only 1 byte long when sent
118                  * by the client
119                  */
120                  word = malloc(2);
121                  *word = swi.dhcp;
122                  at->data = word;
123                  at->size = 2;
124                 break;
125         
126         case ATTR_ENCPASS:
127                 if (!swi.encpass)
128                         break;
129                 dword = malloc(4);
130                 *dword = 1;
131                 at->data = dword;
132                 at->size = 4;
133                 break;
134         
135         case ATTR_PORT_STATUS:
136                 for (p = 1; p <= swi.ports; p++) {
137                         apu = malloc(sizeof(*apu));
138                         apu->port = p;
139                         apu->status = SPEED_1000;
140                         apu->unk = 0;
141                         
142                         pushFrontList(attr, newAttr(ATTR_PORT_STATUS, sizeof(*apu), apu));
143                 }
144                 return 1;
145         
146         case ATTR_PORT_STATISTICS:
147                 for (p = 1; p <= swi.ports; p++) {
148                         api = malloc(sizeof(*api));
149                         memset(api, 0, sizeof(*api));
150                         api->port = p;
151                         api->recv = p * 100000;
152                         api->sent = p * 200000;
153                         
154                         pushFrontList(attr, newAttr(ATTR_PORT_STATISTICS, sizeof(*api), api));
155                 }
156                 return 1;
157         }
158         
159         return 0;
160 }
161
162
163 static int process_write_attr (struct nsdp_cmd *nc, List *attr, struct attr *at)
164 {
165         char *text;
166         
167         
168         if (at->size == 0)
169                 return -EMSGSIZE;
170         
171         switch (at->attr) {
172         
173         case ATTR_NEW_PASSWORD:
174                 if (swi.encpass)
175                         passwordEndecode(at->data, at->size);
176                 text = at->data;
177                 trim(text, MAX_STR_SIZE);
178                 strncpy(swi.password, text, MAX_STR_SIZE);
179                 break;
180         }
181         
182         return 1;
183 }
184
185
186 static int check_password (struct nsdp_cmd *nc, List *attr)
187 {
188         ListNode *ln = attr->first;
189         struct attr *at = NULL;
190         char *text;
191         
192         
193         /* in a write request, the password attribute must be present
194          * and the first element of the list
195          * official win app never sends password inside a read request, but
196          * ngadmin does that because in that case the password is not echoed
197          * back by the switch, so we have to support that, though password in
198          * such requests are not mandatory
199          */
200         if (ln == NULL || (at = ln->data)->attr != ATTR_PASSWORD) {
201                 if (nc->code == CODE_WRITE_REQ)
202                         nc->error = ERROR_DENIED;
203                 goto end;
204         }
205         
206         if (at->size == 0) {
207                 nc->error = ERROR_DENIED;
208                 goto end;
209         }
210         
211         /* normally, we would expect password encryption to be handled
212          * in read requests as well as in write requests
213          * but it seems that real Netgear switches that support
214          * password encryption do NOT accept encrypted passwords in
215          * read requests
216          * this seems more to be a bug in their firmwares, however, as
217          * the scope of this program is to simulate a switch, we adopt
218          * the same buggy behaviour
219          */
220         if (nc->code == CODE_WRITE_REQ && swi.encpass)
221                 passwordEndecode(at->data, at->size);
222         text = at->data;
223         text[at->size] = '\0';
224         trim(text, at->size);
225         if (strcmp(text, swi.password) != 0)
226                 nc->error = ERROR_DENIED;
227         
228         
229 end:
230         if (nc->error == 0) {
231                 /* correct password */
232                 if (ln != NULL && at->attr == ATTR_PASSWORD)
233                         destroyElement(attr, ln, (void(*)(void*))freeAttr);
234                 return 0;
235         } else {
236                 /* invalid password, empty list */
237                 nc->attr_error = ATTR_PASSWORD;
238                 clearList(attr, (void(*)(void*))freeAttr);
239                 return -EACCES;
240         }
241 }
242
243
244 static void process_packet (struct nsdp_cmd *nc, List *attr)
245 {
246         struct ListNode *ln, *pr;
247         struct attr *at;
248         int err;
249         int (*process_attr)(struct nsdp_cmd *, List *, struct attr *);
250         
251         
252         check_password(nc, attr);
253         if (nc->code == CODE_READ_REQ) {
254                 nc->code = CODE_READ_REP;
255                 process_attr = process_read_attr;
256         } else if (nc->code == CODE_WRITE_REQ) {
257                 nc->code = CODE_WRITE_REP;
258                 process_attr = process_write_attr;
259         } else {
260                 /* invalid operation code */
261                 return;
262         }
263         
264         
265         for (ln = attr->first; ln != NULL; ) {
266                 at = ln->data;
267                 
268                 err = process_attr(nc, attr, at);
269                 
270                 if (err == 1) {
271                         /* destroy current attribute */
272                         pr = ln;
273                         ln = ln->next;
274                         destroyElement(attr, pr, (void(*)(void*))freeAttr);
275                 } else if (err == 0) {
276                         /* keep current attribute */
277                         ln = ln->next;
278                 } else {
279                         /* error, abort */
280                         return;
281                 }
282         }
283         
284         memcpy(&nc->switch_mac, swi.mac, ETH_ALEN);
285         nc->remote_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
286         
287         sendNsdpPacket(sock, nc, attr);
288 }
289
290
291 static int netdev_info (const char *dev)
292 {
293         struct ifreq ifr;
294         int err;
295         
296         
297         memset(&ifr, 0, sizeof(struct ifreq));
298         strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
299         
300         /* get interface MAC */
301         err = ioctl(sock, SIOCGIFHWADDR, &ifr, sizeof(struct ifreq));
302         if (err < 0) {
303                 perror("ioctl(SIOCGIFHWADDR)");
304                 return err;
305         }
306         memcpy(swi.mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
307         
308         /* get interface IP */
309         err = ioctl(sock, SIOCGIFADDR, &ifr, sizeof(struct ifreq));
310         if (err < 0) {
311                 perror("ioctl(SIOCGIFADDR)");
312                 return err;
313         }
314         swi.ip = (*(struct sockaddr_in*)&ifr.ifr_addr).sin_addr.s_addr;
315         
316         /* get interface netmask */
317         err = ioctl(sock, SIOCGIFNETMASK, &ifr, sizeof(struct ifreq));
318         if (err < 0) {
319                 perror("ioctl(SIOCGIFNETMASK)");
320                 return err;
321         }
322         swi.netmask = (*(struct sockaddr_in*)&ifr.ifr_netmask).sin_addr.s_addr;
323         
324         /* TODO: get netmask */
325         swi.gw = htonl(ntohl(swi.ip & swi.netmask) | 1);
326         
327         
328         return 0;
329 }
330
331
332 static void handler (int sig)
333 {
334         (void)sig;
335         printf("interrupt\n");
336 }
337
338
339 int main (int argc, char **argv)
340 {
341         static const struct option options[] = {
342                 {"help", no_argument, NULL, 'h'},
343                 {"interface", required_argument, NULL, 'i'},
344                 {0, 0, 0, 0}
345         };
346         int err = 0, len;
347         const char *iface = "eth0";
348         struct sockaddr_in local;
349         struct sigaction sa;
350         struct nsdp_cmd nc;
351         List *attr;
352         
353         
354         opterr = 0;
355         
356         while ((len = getopt_long(argc, argv, "hi:", options, NULL)) != -1) {
357                 switch (len) {
358                 
359                 case 'h':
360                         printf("usage: %s [-h] [-i <interface>]\n", argv[0]);
361                         goto end;
362                 
363                 case 'i':
364                         iface = optarg;
365                         break;
366                 
367                 case '?':
368                         printf("unknown option: \"%s\"\n", argv[optind - 1]);
369                         goto end;
370                 }
371         }
372         
373         
374         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
375         if (sock < 0) {
376                 perror("socket");
377                 err = 1;
378                 goto end;
379         }
380         
381         memset(&local, 0, sizeof(struct sockaddr_in));
382         local.sin_family = AF_INET;
383         local.sin_addr.s_addr = htonl(INADDR_ANY);
384         local.sin_port = htons(SWITCH_PORT);
385         
386         if (bind(sock, (struct sockaddr*)&local, sizeof(struct sockaddr_in)) < 0) {
387                 perror("bind");
388                 err = 1;
389                 goto end;
390         }
391         
392         /* allow broadcasting */
393         len = 1;
394         len = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &len, sizeof(len));
395         if (len < 0) {
396                 perror("setsockopt(SO_BROADCAST)");
397                 err = 1;
398                 goto end;
399         }
400         
401         /* initialize switch infos */
402         memset(&swi, 0, sizeof(struct swi_info));
403         strncpy(swi.product, "NgEmu_0.1", MAX_STR_SIZE);
404         strncpy(swi.name, "Netgear Switch Emulator", MAX_STR_SIZE);
405         strncpy(swi.firmware, "0.1", MAX_STR_SIZE);
406         strncpy(swi.password, "password", MAX_STR_SIZE);
407         swi.encpass = true;
408         swi.ports = 8;
409         swi.dhcp = false;
410         if (netdev_info(iface) < 0)
411                 goto end;
412         
413         
414         attr = createEmptyList();
415         
416         memset(&sa, 0, sizeof(struct sigaction));
417         sa.sa_handler = handler;
418         sigaction(SIGINT, &sa, NULL);
419         sigaction(SIGTERM, &sa, NULL);
420         
421         while (1) {
422                 memset(&nc, 0, sizeof(struct nsdp_cmd));
423                 nc.remote_addr.sin_family = AF_INET;
424                 nc.remote_addr.sin_port = htons(CLIENT_PORT);
425                 
426                 len = recvNsdpPacket(sock, &nc, attr, NULL);
427                 if (len < 0)
428                         break;
429                 
430                 /* ignore packets not for us */
431                 for (len = 0; nc.switch_mac.ether_addr_octet[len] == 0 && len < ETH_ALEN; len++);
432                 if (len < ETH_ALEN && memcmp(swi.mac, &nc.switch_mac, ETH_ALEN) != 0)
433                         continue;
434                 
435                 process_packet(&nc, attr);
436                 clearList(attr, (void(*)(void*))freeAttr);
437         }
438         
439         destroyList(attr, (void(*)(void*))freeAttr);
440         
441 end:
442         close(sock);
443         
444         return err;
445 }
446
447