packet.c 12.6 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) {
K
Kozlov Dmitry 已提交
128 129 130
			rad_packet_free(pack);
			if (errno == EAGAIN)
				return -1;
131
			log_ppp_error("radius:packet:read: %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
132 133 134 135 136 137
			goto out_err;
		}
		break;
	}

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

	ptr = (uint8_t *)pack->buf;

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

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

	ptr += 16;
	n -= 20;

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

K
Kozlov Dmitry 已提交
225 226 227
	*p = pack;

	return 0;
K
Kozlov Dmitry 已提交
228 229 230

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

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

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

249
	mempool_free(pack);
K
Kozlov Dmitry 已提交
250
}
251 252 253

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

	list_for_each_entry(attr, &pack->attrs, entry) {
301 302 303 304
		if (attr->vendor)
			print("<%s %s ", attr->vendor->name, attr->attr->name);
		else
			print(" <%s ", attr->attr->name);
305 306 307 308 309 310 311 312 313 314 315 316 317 318
		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;
319 320 321 322 323 324
		}
		print(">");
	}
	print("]\n");
}

325 326 327 328 329 330 331 332 333 334 335 336
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;
	
337
	ra = mempool_alloc(attr_pool);
338 339 340
	if (!ra)
		return -1;

341
	memset(ra, 0, sizeof(*ra));
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
	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;
}

int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, uint8_t *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;
	
376
	ra = mempool_alloc(attr_pool);
377
	if (!ra) {
378
		log_emerg("radius: out of memory\n");
379 380 381
		return -1;
	}

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

	return 0;
}
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;
	
409
	ra = mempool_alloc(attr_pool);
410
	if (!ra) {
411
		log_emerg("radius: out of memory\n");
412 413 414
		return -1;
	}

415
	memset(ra, 0, sizeof(*ra));
416 417
	ra->attr = attr;
	ra->len = len;
D
Dmitry Kozlov 已提交
418
	ra->val.string = _malloc(len+1);
419
	if (!ra->val.string) {
420
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
421
		_free(ra);
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
		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 已提交
444
		ra->val.string = _realloc(ra->val.string, len + 1);
445
		if (!ra->val.string) {
446
			log_emerg("radius: out of memory\n");
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
			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;
	
477
	ra = mempool_alloc(attr_pool);
478 479 480
	if (!ra)
		return -1;

481
	memset(ra, 0, sizeof(*ra));
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 508
	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;
}

509 510 511 512 513 514
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);
}


515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
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;
538
			log_ppp_error("radius:write: %s\n", strerror(errno));
539 540
			return -1;
		} else if (n != pack->len) {
541
			log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len);
542 543 544 545 546 547 548 549
			return -1;
		}
		break;
	}

	return 0;
}

550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
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;
	
567
	ra = mempool_alloc(attr_pool);
568 569 570 571 572 573 574 575 576
	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 已提交
577
	ra->val.octets = _malloc(len);
578 579
	if (!ra->val.octets) {
		log_emerg("radius: out of memory\n");
D
Dmitry Kozlov 已提交
580
		_free(ra);
581 582 583 584 585 586 587 588 589
		return -1;
	}
	memcpy(ra->val.octets, val, len);
	list_add_tail(&ra->entry, &pack->attrs);
	pack->len += 6 + 2 + len;

	return 0;
}

590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
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;
}
607 608 609 610 611 612

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