1 /*
2 plconfig.c version 0.2
3 Source code for Intellon-based Powerline bridge configuration tool
4
5 Copyright (C) 2002-2003 Manuel Kasper <mk@neon1.net>.
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10
11 1. Redistributions of source code must retain the above copyright notice,
12 this list of conditions and the following disclaimer.
13
14 2. Redistributions in binary form must reproduce the above copyright
15 notice, this list of conditions and the following disclaimer in the
16 documentation and/or other materials provided with the distribution.
17
18 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/ioctl.h>
33 #include <net/bpf.h>
34 #include <sys/socket.h>
35 #include <net/if.h>
36 #include <stdio.h>
37 #include <sys/uio.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include "global.h"
42 #include "md5.h"
43
44 #define PLCONFIG_VERSION "0.2"
45 #define ETHERTYPE_INTELLON 0x887b
46
47 #define logictostr(x) (x) ? "yes" : "no"
48
49 /* bpf instructions to filter for Intellon ethertype packets */
50 struct bpf_insn insns[] = {
51 BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
52 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_INTELLON, 0, 1),
53 BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
54 BPF_STMT(BPF_RET+BPF_K, 0)
55 };
56
ex_word(u_char * ptr)57 u_short ex_word(u_char *ptr) {
58 return ntohs(*((u_short*)ptr));
59 }
60
ex_long(u_char * ptr)61 u_long ex_long(u_char *ptr) {
62 return ntohl(*((u_long*)ptr));
63 }
64
format_mac_addr(u_char * addr,char * macbuf)65 char *format_mac_addr(u_char *addr, char *macbuf) {
66
67 sprintf(macbuf, "%02x:%02x:%02x:%02x:%02x:%02x",
68 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
69
70 return macbuf;
71 }
72
dump_params_and_stats(u_char * macmgmt)73 void dump_params_and_stats(u_char *macmgmt) {
74
75 printf(" Tx ACK Counter: %u\n"
76 " Tx NACK Counter: %u\n"
77 " Tx FAIL Counter: %u\n"
78 " Tx Contention Loss Counter: %u\n"
79 " Tx Collision Counter: %u\n"
80 " Tx CA3 Latency Counter: %u\n"
81 " Tx CA2 Latency Counter: %u\n"
82 " Tx CA1 Latency Counter: %u\n"
83 " Tx CA0 Latency Counter: %u\n"
84 " Rx Cumul. Bytes per 40-symbol Packet Counter: %lu\n",
85
86 ex_word(&macmgmt[2]), ex_word(&macmgmt[4]), ex_word(&macmgmt[6]),
87 ex_word(&macmgmt[8]), ex_word(&macmgmt[10]), ex_word(&macmgmt[12]),
88 ex_word(&macmgmt[14]), ex_word(&macmgmt[16]), ex_word(&macmgmt[18]),
89 ex_long(&macmgmt[20]));
90 }
91
dump_network_statistics(u_char * macmgmt)92 void dump_network_statistics(u_char *macmgmt) {
93
94 int da;
95 u_char *stat;
96 char macbuf[20];
97
98 for (da = 0; da < 15; da++) {
99
100 stat = (macmgmt+9+da*12);
101
102 /* Check to see if that node entry is valid -
103 stupid Intellon chip is supposed to return 00:00:00:00:00:00 for
104 nonexistant nodes (as per the specs), but instead it returns
105 01:00:00:00:00:00 so we just skip checking the first byte,
106 since heaven knows what else it may return instead of 01 in
107 other places/revisions.
108 */
109
110 if (!((stat[1] == 0) && (stat[2] == 0) &&
111 (stat[3] == 0) && (stat[4] == 0) && (stat[5] == 0))) {
112
113 printf("\n Statistics for Network DA #%d:\n"
114 " MAC address: %s\n"
115 " Bytes in 40 symbols: %u\n"
116 " FAILS received: %u\n"
117 " Frame Drops: %u\n",
118
119 da+1, format_mac_addr(stat, macbuf), ex_word(&stat[6]),
120 ex_word(&stat[8]), ex_word(&stat[10]));
121 }
122 }
123 }
124
dump_tx_characteristics(u_char * macmgmt)125 void dump_tx_characteristics(u_char *macmgmt) {
126
127 char *retrtab[] = {"Transmit without retries",
128 "Transmit with one retry only",
129 "Transmit with normal retries (HomePlug)", "Reserved"};
130
131 printf(" Local consumption only: %s\n"
132 " Encryption flag: %s\n"
133 " Transmit priority: %u\n"
134 " Response expected: %s\n"
135 " Transmit contention free: %s\n"
136 " Retry control: %s\n"
137 " No default encryption receive: %s\n"
138 " No unencrypted receive: %s\n"
139 " Transmit EKS: %u\n",
140
141 logictostr(macmgmt[2] & 0x80), logictostr(macmgmt[2] & 0x40),
142 (macmgmt[2] >> 4) & 0x03, logictostr(macmgmt[2] & 0x08),
143 logictostr(macmgmt[2] & 0x04), retrtab[(macmgmt[3] >> 6) & 0x03],
144 logictostr(macmgmt[3] & 0x08), logictostr(macmgmt[3] & 0x04),
145 macmgmt[4]);
146 }
147
dump_set_key(u_char * macmgmt)148 void dump_set_key(u_char *macmgmt) {
149
150 char asckey[17];
151 char *hextab = "0123456789abcdef";
152 int i;
153
154 /* Convert the key to ASCII hex */
155 for (i = 0; i < 8; i++) {
156 asckey[i<<1] = hextab[(macmgmt[i+3] >> 4) & 0x0F];
157 asckey[(i<<1)+1] = hextab[macmgmt[i+3] & 0x0F];
158 }
159
160 asckey[16] = 0;
161
162 printf(" Encryption key select: 0x%02x\n"
163 " Network encryption key: %s\n",
164
165 macmgmt[2], asckey);
166
167 }
168
read_display_responses(int netfd,u_char * framebuf,u_int buflen)169 void read_display_responses(int netfd, u_char *framebuf, u_int buflen) {
170
171 u_char *frameptr;
172 ssize_t rdlen;
173 u_int i, j;
174 struct bpf_hdr *header;
175 char macbuf[20];
176
177 /* read responses */
178 while (1) {
179 rdlen = read(netfd, framebuf, buflen);
180
181 if (rdlen != -1) {
182
183 header = (struct bpf_hdr*)framebuf;
184 frameptr = framebuf + header->bh_hdrlen;
185
186 if ((frameptr[12] == 0x88) && (frameptr[13] == 0x7B)) {
187
188 /* It's an intellon packet - read MAC management entries */
189 j = 15;
190
191 for (i = 0; i < (frameptr[14] & 0x7F); i++) {
192 switch (frameptr[j]) {
193
194 case 0x04: /* Set Network Encryption Key */
195 printf("\n- Set Network Encryption Key from %s\n",
196 format_mac_addr(&frameptr[6], macbuf));
197
198 dump_set_key(&frameptr[j]);
199 break;
200
201 case 0x07: /* Request Parameters and Statistics */
202 printf("\n- Parameters and Statistics request from %s\n",
203 format_mac_addr(&frameptr[6], macbuf));
204 break;
205
206 case 0x08: /* Parameters and Statistics Response */
207 printf("\n- Parameters and Statistics response from %s\n",
208 format_mac_addr(&frameptr[6], macbuf));
209
210 dump_params_and_stats(&frameptr[j]);
211 break;
212
213 case 0x06: /* Confirm Network Encryption Key */
214 printf("\n- Network encryption key confirmation from %s\n",
215 format_mac_addr(&frameptr[6], macbuf));
216 break;
217
218 case 0x1a: /* Intellon specific network statistics */
219 if (!(frameptr[j+2] & 0x80)) { /* Really a response? */
220 printf("\n- Intellon-specific network statistics from %s\n",
221 format_mac_addr(&frameptr[6], macbuf));
222
223 dump_network_statistics(&frameptr[j]);
224 }
225 break;
226
227 case 0x1f: /* Set transmit characteristics */
228 printf("\n- Set transmit characteristics from %s\n",
229 format_mac_addr(&frameptr[6], macbuf));
230
231 dump_tx_characteristics(&frameptr[j]);
232 break;
233
234
235 default:
236 printf("- Unknown response (MTYPE = 0x%02x) from %s\n",
237 frameptr[j], format_mac_addr(&frameptr[6], macbuf));
238 }
239 j += frameptr[j+1] + 2;
240 }
241 }
242 }
243 }
244 }
245
deskeyparity(unsigned char kb)246 unsigned char deskeyparity(unsigned char kb) {
247 unsigned char parity = 0, i, mykb = kb;
248
249 for (i = 0; i < 7; i++) {
250 mykb >>= 1;
251 parity += (mykb & 0x01);
252 }
253
254 return ((kb & 0xFE) | (~parity & 0x01));
255 }
256
usage(void)257 void usage(void) {
258
259 printf("%s",
260 "\nPowerline Bridge config version " PLCONFIG_VERSION " by Manuel Kasper <mk@neon1.net>\n\n"
261 "Usage: plconfig [-pqrh] [-b device] [-s key] interface\n\n"
262
263 " -s key set network encryption key\n"
264 " (plaintext password or 8 hex bytes preceded by 0x)\n"
265 " -b device use device (default is /dev/bpf0)\n"
266 " -p don't switch interface to promiscuous mode\n"
267 " -r request parameters and statistics\n"
268 " -q request Intellon-specific network statistics\n"
269 " -h display this help\n\n"
270
271 " If -s is not specified, plconfig will listen for management packets\n"
272 " indefinitely (after requesting stats if -r is specified)\n\n");
273 }
274
main(int argc,char * argv[])275 int main(int argc, char *argv[]) {
276 int netfd, ch;
277 struct ifreq ifr;
278 struct bpf_program filter;
279 u_int buflen, i;
280 u_char *framebuf;
281 char ifname[8], bpfn[32] = "/dev/bpf0";
282 u_char netkey[8], nib, outframe[200];
283
284 /* options */
285 int nopromisc = 0, mode = 0;
286
287 /* Parse command line options */
288 while ((ch = getopt(argc, argv, "s:b:pqrh")) != -1) {
289
290 switch (ch) {
291
292 case 'p':
293 nopromisc = 1;
294 break;
295
296 case 'r':
297 mode = 1;
298 break;
299
300 case 'q':
301 mode = 3;
302 break;
303
304 case 's':
305 mode = 2;
306
307 /* See if it begins with 0x */
308 if ((optarg[0] == '0') && (optarg[1] == 'x')) {
309
310 for (i = 0; i < 8; i++)
311 netkey[i] = 0;
312
313 /* convert ASCII hex to binary */
314 for (i = 0; i < 16; i++) {
315 if ((optarg[i+2] >= '0') && (optarg[i+2] <= '9')) {
316 nib = optarg[i+2] - '0';
317 } else if ((optarg[i+2] >= 'a') && (optarg[i+2] <= 'f')) {
318 nib = optarg[i+2] + 0x0a - 'a';
319 } else if ((optarg[i+2] >= 'A') && (optarg[i+2] <= 'F')) {
320 nib = optarg[i+2] + 0x0a - 'A';
321 } else {
322 fprintf(stderr, "Unrecognized character '%c' in key\n", optarg[i+2]);
323 exit(1);
324 }
325
326 if (i & 0x01)
327 netkey[i >> 1] |= nib;
328 else
329 netkey[i >> 1] |= (nib << 4);
330 }
331 } else {
332 /* It's a plaintext password - use PBKDF1 on it */
333 MD5_CTX md5ctx;
334 u_char digest[16], tmp[256];
335
336 strncpy(tmp, optarg, 240);
337 /* add salt */
338 strcat(tmp, "\x08\x85\x6d\xaf\x7c\xf5\x81\x85");
339
340 /* generate initial digest */
341 MD5Init(&md5ctx);
342 MD5Update(&md5ctx, tmp, strlen(tmp));
343 MD5Final(digest, &md5ctx);
344
345 /* loop 999 times as required by HomePlug */
346 for (i = 0; i < 999; i++) {
347 MD5Init(&md5ctx);
348 MD5Update(&md5ctx, digest, 16);
349 MD5Final(digest, &md5ctx);
350 }
351
352 /* extract the first 8 bytes; calculate parity bit
353 (LSB = odd parity), even though most powerline bridges
354 seem to ignore it
355 */
356 for (i = 0; i < 8; i++)
357 netkey[i] = deskeyparity(digest[i]);
358 }
359 break;
360
361 case 'b':
362 strncpy(bpfn, optarg, 32);
363 break;
364
365 case '?':
366 case 'h':
367 default:
368 usage();
369 exit(0);
370 }
371 }
372
373 argc -= optind;
374 argv += optind;
375
376 if (argc != 1) {
377 usage();
378 exit(0);
379 }
380
381 strncpy(ifname, argv[0], 8);
382
383 /* Open bpf device */
384 netfd = open(bpfn, O_RDWR);
385
386 if (netfd == -1) {
387 fprintf(stderr, "Cannot open %s\n", bpfn);
388 exit(0);
389 }
390
391 /* Read buffer length */
392 if (ioctl(netfd, BIOCGBLEN, &buflen) == -1) {
393 fprintf(stderr, "ioctl(BIOCGBLEN) error!\n");
394 exit(0);
395 }
396
397 /* Allocate buffer */
398 if (!(framebuf = (u_char*)malloc((size_t)buflen))) {
399 fprintf(stderr, "Cannot malloc() packet buffer!\n");
400 exit(0);
401 }
402
403 /* Bind to interface */
404 strcpy(ifr.ifr_name, ifname);
405
406 if (ioctl(netfd, BIOCSETIF, &ifr) == -1) {
407 fprintf(stderr, "ioctl(BIOCSETIF) error!\n");
408 exit(0);
409 }
410
411 /* Set filter */
412 filter.bf_len = sizeof(insns) / sizeof(insns[0]);
413 filter.bf_insns = insns;
414
415 if (ioctl(netfd, BIOCSETF, &filter) == -1) {
416 fprintf(stderr, "ioctl(BIOCSETF) error!\n");
417 exit(0);
418 }
419
420 /* Set immediate mode */
421 i = 1;
422 if (ioctl(netfd, BIOCIMMEDIATE, &i) == -1) {
423 fprintf(stderr, "ioctl(BIOCIMMEDIATE) error!\n");
424 exit(0);
425 }
426
427 /* Set promiscuous mode
428 This is necessary because the bridges seem to be returning
429 responses with the destination MAC address set to their own
430 MAC address instead of using broadcasts.
431 */
432 if ((!nopromisc) && (mode != 2)) {
433 i = 1;
434 if (ioctl(netfd, BIOCPROMISC, &i) == -1) {
435 fprintf(stderr, "ioctl(BIOCPROMISC) error!\n");
436 exit(0);
437 }
438 }
439
440 /* We don't want to see local packets */
441 i = 0;
442 if (ioctl(netfd, BIOCGSEESENT, &i) == -1) {
443 fprintf(stderr, "ioctl(BIOCGSEESENT) error!\n");
444 exit(0);
445 }
446
447 if (mode) {
448 /* set up outgoing command frame */
449 for (i = 0; i < 6; i++)
450 outframe[i] = 0xFF; /* broadcast */
451
452 for (i = 0; i < 6; i++)
453 outframe[i+6] = 0x00; /* the source address will be set automatically */
454
455 outframe[12] = 0x88; /* Intellon ethertype */
456 outframe[13] = 0x7b;
457
458 outframe[14] = 0x01; /* one MAC management entry */
459 }
460
461 switch (mode) {
462
463 case 1: /* request parameters & statistics */
464 outframe[15] = 0x07; /* request parameters and statistics */
465 outframe[16] = 0x0; /* 0 bytes follow */
466
467 /* fill the rest with zeroes to maintain minimum data payload of 46 bytes */
468 for (i = 0; i < 43; i++)
469 outframe[i+17] = 0x00;
470
471 /* write out packet */
472 write(netfd, outframe, 60);
473 break;
474
475 case 2: /* set network key */
476 outframe[15] = 0x04; /* set network key */
477 outframe[16] = 0x09; /* 9 bytes follow */
478 outframe[17] = 0x01; /* encryption key select -> 1 */
479
480 for (i = 0; i < 8; i++)
481 outframe[i+18] = netkey[i];
482
483 /* fill the rest with zeroes to maintain minimum data payload of 46 bytes */
484 for (i = 0; i < 34; i++)
485 outframe[i+26] = 0x00;
486
487 /* write out packet */
488 write(netfd, outframe, 60);
489 break;
490
491 case 3: /* request Intellon-specific network statistics */
492 outframe[15] = 0x1a; /* request network statistics */
493 outframe[16] = 0xbb; /* 187 bytes follow */
494
495 outframe[17] = 0x80; /* read the stats, don't clear them */
496
497 for (i = 0; i < 186; i++)
498 outframe[i+18] = 0x00;
499
500 /* write out packet */
501 write(netfd, outframe, 204);
502 break;
503 }
504
505 if (mode != 2)
506 read_display_responses(netfd, framebuf, buflen);
507
508 free(framebuf);
509
510 /* Close bpf device */
511 close(netfd);
512 }
513