packet.c 13.7 KB
Newer Older
K
Kozlov Dmitry 已提交
1 2 3 4
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
D
Dmitry Kozlov 已提交
5 6
#include <stdio.h>
#include <unistd.h>
7
#include <fcntl.h>
D
Dmitry Kozlov 已提交
8 9

#include "log.h"
10
#include "mempool.h"
K
Kozlov Dmitry 已提交
11

12
#include "radius_p.h"
K
Kozlov Dmitry 已提交
13

D
Dmitry Kozlov 已提交
14 15
#include "memdebug.h"

16 17 18
static mempool_t packet_pool;
static mempool_t attr_pool;

19 20 21 22
struct rad_packet_t *rad_packet_alloc(int code)
{
	struct rad_packet_t *pack;

23
	pack = mempool_alloc(packet_pool);
24
	if (!pack) {
25
		log_emerg("radius:packet: out of memory\n");
26 27 28 29 30 31 32 33 34 35 36
		return NULL;
	}

	memset(pack, 0, sizeof(*pack));
	pack->code = code;
	pack->len = 20;
	pack->id = 1;
	INIT_LIST_HEAD(&pack->attrs);

	return pack;
}
K
Kozlov Dmitry 已提交
37

38 39 40 41 42 43 44 45 46
void print_buf(uint8_t *buf,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%x ",buf[i]);
	printf("\n");
}

int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
K
Kozlov Dmitry 已提交
47
{
48
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
49 50
	uint8_t *ptr;

51
	if (pack->buf)
D
Dmitry Kozlov 已提交
52
		ptr = _realloc(pack->buf, pack->len);
53
	else
D
Dmitry Kozlov 已提交
54
		ptr = _malloc(pack->len);
55

K
Kozlov Dmitry 已提交
56
	if (!ptr) {
57
		log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
58 59 60
		return -1;
	}
	
61
	pack->buf = ptr;
K
Kozlov Dmitry 已提交
62 63
	*ptr = pack->code; ptr++;
	*ptr = pack->id; ptr++;
64 65
	*(uint16_t*)ptr = htons(pack->len); ptr+= 2;
	memcpy(ptr, RA, 16);	ptr+=16;
K
Kozlov Dmitry 已提交
66 67

	list_for_each_entry(attr, &pack->attrs, entry) {
68 69 70 71 72
		if (attr->vendor) {
			*ptr = 26; ptr++;
			*ptr = attr->len + 2 + 6; ptr++;
			*(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4;
		} 
73 74
		*ptr = attr->attr->id; ptr++;
		*ptr = attr->len + 2; ptr++;
D
Dmitry Kozlov 已提交
75
		switch(attr->attr->type) {
K
Kozlov Dmitry 已提交
76
			case ATTR_TYPE_INTEGER:
77
				*(uint32_t*)ptr = htonl(attr->val.integer);
K
Kozlov Dmitry 已提交
78
				break;
79
			case ATTR_TYPE_OCTETS:
K
Kozlov Dmitry 已提交
80
			case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
81
				memcpy(ptr, attr->val.string, attr->len);
K
Kozlov Dmitry 已提交
82 83 84 85 86
				break;
			case ATTR_TYPE_IPADDR:
				*(in_addr_t*)ptr = attr->val.ipaddr;
				break;
			case ATTR_TYPE_DATE:
87
				*(uint32_t*)ptr = htonl(attr->val.date);
K
Kozlov Dmitry 已提交
88 89
				break;
			default:
90
				log_emerg("radius:packet:BUG: unknown attribute type\n");
K
Kozlov Dmitry 已提交
91 92 93 94 95
				abort();
		}
		ptr += attr->len;
	}

96
	//print_buf(pack->buf, pack->len);
97
	return 0;
K
Kozlov Dmitry 已提交
98 99
}

K
Kozlov Dmitry 已提交
100
int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr)
K
Kozlov Dmitry 已提交
101 102
{
	struct rad_packet_t *pack;
103
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
104
	struct rad_dict_attr_t *da;
105
	struct rad_dict_vendor_t *vendor;
K
Kozlov Dmitry 已提交
106
	uint8_t *ptr;
107
	int n, id, len, vendor_id;
108
	socklen_t addr_len = sizeof(*addr);
K
Kozlov Dmitry 已提交
109

K
Kozlov Dmitry 已提交
110 111
	*p = NULL;

112 113
	pack = rad_packet_alloc(0);
	if (!pack)
K
Kozlov Dmitry 已提交
114
		return 0;
K
Kozlov Dmitry 已提交
115

D
Dmitry Kozlov 已提交
116
	pack->buf = _malloc(REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
117
	if (!pack->buf) {
118
		log_emerg("radius:packet: out of memory\n");
119
		goto out_err;
K
Kozlov Dmitry 已提交
120 121 122
	}

	while (1) {
123 124 125 126
		if (addr)
			n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len);
		else
			n = read(fd, pack->buf, REQ_LENGTH_MAX);
K
Kozlov Dmitry 已提交
127
		if (n < 0) {
128 129
			if (errno == EAGAIN) {
				rad_packet_free(pack);
K
Kozlov Dmitry 已提交
130
				return -1;
131 132 133
			}
			if (errno != ECONNREFUSED)
				log_ppp_error("radius:packet:read: %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
134 135 136 137 138 139
			goto out_err;
		}
		break;
	}

	if (n < 20) {
140
		log_ppp_warn("radius:packet: short packed received (%i)\n", n);
K
Kozlov Dmitry 已提交
141 142 143 144 145 146 147
		goto out_err;
	}

	ptr = (uint8_t *)pack->buf;

	pack->code = *ptr; ptr++;
	pack->id = *ptr; ptr++;
148
	pack->len = ntohs(*(uint16_t*)ptr); ptr += 2;
K
Kozlov Dmitry 已提交
149 150

	if (pack->len > n) {
151
		log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n);
K
Kozlov Dmitry 已提交
152 153 154 155 156 157 158
		goto out_err;
	}

	ptr += 16;
	n -= 20;

	while (n>0) {
159 160 161
		id = *ptr; ptr++;
		len = *ptr - 2; ptr++;
		if (len < 0) {
162
			log_ppp_warn("radius:packet short attribute len received\n");
163 164
			goto out_err;
		}
K
Kozlov Dmitry 已提交
165
		if (2 + len > n) {
166
			log_ppp_warn("radius:packet: too long attribute received (%i, %i)\n", id, len);
K
Kozlov Dmitry 已提交
167 168
			goto out_err;
		}
169 170 171 172 173 174 175
		if (id == 26) {
			vendor_id = ntohl(*(uint32_t *)ptr);
			vendor = rad_dict_find_vendor_id(vendor_id);
			if (vendor) {
				ptr += 4;
				id = *ptr; ptr++;
				len = *ptr - 2; ptr++;
176
				n -= 2 + 4;
177
			} else
178
				log_ppp_warn("radius:packet: vendor %i not found\n", id);
179 180
		} else
			vendor = NULL;
181
		da = rad_dict_find_attr_id(vendor, id);
K
Kozlov Dmitry 已提交
182
		if (da) {
183
			attr = mempool_alloc(attr_pool);
K
Kozlov Dmitry 已提交
184
			if (!attr) {
185
				log_emerg("radius:packet: out of memory\n");
K
Kozlov Dmitry 已提交
186 187
				goto out_err;
			}
188 189
			memset(attr, 0, sizeof(*attr));
			attr->vendor = vendor;
K
Kozlov Dmitry 已提交
190 191
			attr->attr = da;
			attr->len = len;
192 193
			switch (da->type) {
				case ATTR_TYPE_STRING:
D
Dmitry Kozlov 已提交
194
					attr->val.string = _malloc(len+1);
195
					if (!attr->val.string) {
196
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
197
						_free(attr);
198 199 200 201 202
						goto out_err;
					}
					memcpy(attr->val.string, ptr, len);
					attr->val.string[len] = 0;
					break;
203
				case ATTR_TYPE_OCTETS:
D
Dmitry Kozlov 已提交
204
					attr->val.octets = _malloc(len);
205
					if (!attr->val.octets) {
206
						log_emerg("radius:packet: out of memory\n");
D
Dmitry Kozlov 已提交
207
						_free(attr);
208 209 210 211
						goto out_err;
					}
					memcpy(attr->val.octets, ptr, len);
					break;				
212 213 214 215 216 217 218 219
				case ATTR_TYPE_DATE:
				case ATTR_TYPE_INTEGER:
					attr->val.integer = ntohl(*(uint32_t*)ptr);
					break;
				case ATTR_TYPE_IPADDR:
					attr->val.integer = *(uint32_t*)ptr;
					break;
			}
K
Kozlov Dmitry 已提交
220 221
			list_add_tail(&attr->entry, &pack->attrs);
		} else
222
			log_ppp_warn("radius:packet: unknown attribute received (%i,%i)\n", vendor ? vendor->id : 0, id);
K
Kozlov Dmitry 已提交
223 224 225 226
		ptr += len;
		n -= 2 + len;
	}

K
Kozlov Dmitry 已提交
227 228 229
	*p = pack;

	return 0;
K
Kozlov Dmitry 已提交
230 231 232

out_err:
	rad_packet_free(pack);
K
Kozlov Dmitry 已提交
233
	return 0;
K
Kozlov Dmitry 已提交
234 235 236 237
}

void rad_packet_free(struct rad_packet_t *pack)
{
238
	struct rad_attr_t *attr;
K
Kozlov Dmitry 已提交
239 240
	
	if (pack->buf)
D
Dmitry Kozlov 已提交
241
		_free(pack->buf);
K
Kozlov Dmitry 已提交
242 243 244 245

	while(!list_empty(&pack->attrs)) {
		attr = list_entry(pack->attrs.next, typeof(*attr), entry);
		list_del(&attr->entry);
D
Dmitry Kozlov 已提交
246 247
		if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS)
			_free(attr->val.string);
248
		mempool_free(attr);
K
Kozlov Dmitry 已提交
249 250
	}

251
	mempool_free(pack);
K
Kozlov Dmitry 已提交
252
}
253 254 255

void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...))
{
256
	struct rad_attr_t *attr;
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	struct rad_dict_value_t *val;
	
	print("[RADIUS ");
	switch(pack->code) {
		case CODE_ACCESS_REQUEST:
			print("Access-Request");
			break;
		case CODE_ACCESS_CHALLENGE:
			print("Access-Challenge");
			break;
		case CODE_ACCESS_ACCEPT:
			print("Access-Accept");
			break;
		case CODE_ACCESS_REJECT:
			print("Access-Reject");
			break;
273
		case CODE_ACCOUNTING_REQUEST:
274
			print("Accounting-Request");
275 276
			break;
		case CODE_ACCOUNTING_RESPONSE:
277
			print("Accounting-Response");
278
			break;
279
		case CODE_DISCONNECT_REQUEST:
280
			print("Disconnect-Request");
281 282
			break;
		case CODE_DISCONNECT_ACK:
283
			print("Disconnect-ACK");
284 285
			break;
		case CODE_DISCONNECT_NAK:
286
			print("Disconnect-NAK");
287 288
			break;
		case CODE_COA_REQUEST:
289
			print("CoA-Request");
290 291
			break;
		case CODE_COA_ACK:
292
			print("CoA-ACK");
293 294
			break;
		case CODE_COA_NAK:
295
			print("CoA-NAK");
296
			break;
297 298 299 300 301 302
		default:
			print("Unknown (%i)", pack->code);
	}
	print(" id=%x", pack->id);

	list_for_each_entry(attr, &pack->attrs, entry) {
303 304 305 306
		if (attr->vendor)
			print("<%s %s ", attr->vendor->name, attr->attr->name);
		else
			print(" <%s ", attr->attr->name);
307 308 309 310 311 312 313 314 315 316 317 318 319 320
		switch (attr->attr->type) {
			case ATTR_TYPE_INTEGER:
				val = rad_dict_find_val(attr->attr, attr->val);
				if (val)
					print("%s", val->name);
				else
					print("%i", attr->val.integer);
				break;
			case ATTR_TYPE_STRING:
				print("\"%s\"", attr->val.string);
				break;
			case ATTR_TYPE_IPADDR:
				print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff);
				break;
321 322 323 324 325 326
		}
		print(">");
	}
	print("]\n");
}

327 328 329 330 331 332 333 334 335 336 337 338
int rad_packet_add_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
339
	ra = mempool_alloc(attr_pool);
340 341 342
	if (!ra)
		return -1;

343
	memset(ra, 0, sizeof(*ra));
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	ra->attr = attr;
	ra->len = 4;
	ra->val.integer = val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_int(struct rad_packet_t *pack, const char *name, int val)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	ra->val.integer = val;

	return 0;
}

366
int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, const uint8_t *val, int len)
367 368 369 370 371 372 373 374 375 376 377
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
378
	ra = mempool_alloc(attr_pool);
379
	if (!ra) {
380
		log_emerg("radius: out of memory\n");
381 382 383
		return -1;
	}

384
	memset(ra, 0, sizeof(*ra));
385 386
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
387
	ra->val.octets = _malloc(len);
388
	if (!ra->val.octets) {
389
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
390
		_free(ra);
391 392 393 394 395 396 397 398
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

int rad_packet_change_octets(struct rad_packet_t *pack, const char *name, const uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	if (ra->len != len) {
		if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
			return -1;

		ra->val.octets = _realloc(ra->val.octets, len);
		if (!ra->val.octets) {
			log_emerg("radius: out of memory\n");
			return -1;
		}
	
		pack->len += len - ra->len;
		ra->len = len;
	}

	memcpy(ra->val.octets, val, len);

	return 0;
}


428 429 430 431 432 433 434 435 436 437 438 439
int rad_packet_add_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;

	if (pack->len + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
440
	ra = mempool_alloc(attr_pool);
441
	if (!ra) {
442
		log_emerg("radius: out of memory\n");
443 444 445
		return -1;
	}

446
	memset(ra, 0, sizeof(*ra));
447 448
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
449
	ra->val.string = _malloc(len+1);
450
	if (!ra->val.string) {
451
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
452
		_free(ra);
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
		return -1;
	}
	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + len;

	return 0;
}

int rad_packet_change_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	if (ra->len != len) {
		if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
			return -1;

D
Dmitry Kozlov 已提交
475
		ra->val.string = _realloc(ra->val.string, len + 1);
476
		if (!ra->val.string) {
477
			log_emerg("radius: out of memory\n");
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
			return -1;
		}
	
		pack->len += len - ra->len;
		ra->len = len;
	}

	memcpy(ra->val.string, val, len);
	ra->val.string[len] = 0;

	return 0;
}

int rad_packet_add_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_value_t *v;

	if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
		return -1;

	attr = rad_dict_find_attr(name);
	if (!attr)
		return -1;
	
	v = rad_dict_find_val_name(attr, val);
	if (!v)
		return -1;
	
508
	ra = mempool_alloc(attr_pool);
509 510 511
	if (!ra)
		return -1;

512
	memset(ra, 0, sizeof(*ra));
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	ra->attr = attr;
	ra->len = 4;
	ra->val = v->val;
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 2 + 4;

	return 0;
}

int rad_packet_change_val(struct rad_packet_t *pack, const char *name, const char *val)
{
	struct rad_attr_t *ra;
	struct rad_dict_value_t *v;
	
	ra = rad_packet_find_attr(pack, name);
	if (!ra)
		return -1;

	v = rad_dict_find_val_name(ra->attr, val);
	if (!v)
		return -1;

	ra->val = v->val;
	
	return 0;
}

540 541 542 543 544 545
int rad_packet_add_ipaddr(struct rad_packet_t *pack, const char *name, in_addr_t ipaddr)
{
	return rad_packet_add_int(pack, name, ipaddr);
}


546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry)
		if (!strcmp(ra->attr->name, name))
			return ra;

	return NULL;
}

int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
{
	int n;

	while (1) {
		if (addr)
			n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr));
		else
			n = write(fd, pack->buf, pack->len);
		if (n < 0) {
			if (errno == EINTR)
				continue;
569
			log_ppp_error("radius:write: %s\n", strerror(errno));
570 571
			return -1;
		} else if (n != pack->len) {
572
			log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len);
573 574 575 576 577 578 579 580
			return -1;
		}
		break;
	}

	return 0;
}

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
int rad_packet_add_vendor_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	struct rad_dict_attr_t *attr;
	struct rad_dict_vendor_t *vendor;

	if (pack->len + 6 + 2 + len >= REQ_LENGTH_MAX)
		return -1;

	vendor = rad_dict_find_vendor_name(vendor_name);
	if (!vendor)
		return -1;

	attr = rad_dict_find_vendor_attr(vendor, name);
	if (!attr)
		return -1;
	
598
	ra = mempool_alloc(attr_pool);
599 600 601 602 603 604 605 606 607
	if (!ra) {
		log_emerg("radius: out of memory\n");
		return -1;
	}
	
	memset(ra, 0, sizeof(*ra));
	ra->vendor = vendor;
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
608
	ra->val.octets = _malloc(len);
609 610
	if (!ra->val.octets) {
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
611
		_free(ra);
612 613 614 615 616 617 618 619 620
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 6 + 2 + len;

	return 0;
}

621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
int rad_packet_change_vendor_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len)
{
	struct rad_attr_t *ra;
	
	ra = rad_packet_find_vendor_attr(pack, vendor_name, name);
	if (!ra)
		return -1;

	if (ra->len != len) {
		if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
			return -1;

		ra->val.octets = _realloc(ra->val.octets, len);
		if (!ra->val.octets) {
			log_emerg("radius: out of memory\n");
			return -1;
		}
	
		pack->len += len - ra->len;
		ra->len = len;
	}

	memcpy(ra->val.octets, val, len);

	return 0;
}

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
struct rad_attr_t *rad_packet_find_vendor_attr(struct rad_packet_t *pack, const char *vendor_name, const char *name)
{
	struct rad_attr_t *ra;

	list_for_each_entry(ra, &pack->attrs, entry) {
		if (!ra->vendor)
			continue;
		if (strcmp(ra->vendor->name, vendor_name))
			continue;
		if (strcmp(ra->attr->name, name))
			continue;

		return ra;
	}

	return NULL;
}
665 666 667 668 669 670

static void __init init(void)
{
	attr_pool = mempool_create(sizeof(struct rad_attr_t));
	packet_pool = mempool_create(sizeof(struct rad_packet_t));
}