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