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