diff options
Diffstat (limited to 'accel-pppd/radius/packet.c')
-rw-r--r-- | accel-pppd/radius/packet.c | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/accel-pppd/radius/packet.c b/accel-pppd/radius/packet.c new file mode 100644 index 00000000..4e24dedc --- /dev/null +++ b/accel-pppd/radius/packet.c @@ -0,0 +1,644 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include "log.h" +#include "mempool.h" + +#include "radius_p.h" + +#include "memdebug.h" + +static mempool_t packet_pool; +static mempool_t attr_pool; + +struct rad_packet_t *rad_packet_alloc(int code) +{ + struct rad_packet_t *pack; + + pack = mempool_alloc(packet_pool); + if (!pack) { + log_emerg("radius:packet: out of memory\n"); + return NULL; + } + + memset(pack, 0, sizeof(*pack)); + pack->code = code; + pack->len = 20; + pack->id = 1; + INIT_LIST_HEAD(&pack->attrs); + + return pack; +} + +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) +{ + struct rad_attr_t *attr; + uint8_t *ptr; + + if (pack->buf) + ptr = _realloc(pack->buf, pack->len); + else + ptr = _malloc(pack->len); + + if (!ptr) { + log_emerg("radius:packet: out of memory\n"); + return -1; + } + + pack->buf = ptr; + *ptr = pack->code; ptr++; + *ptr = pack->id; ptr++; + *(uint16_t*)ptr = htons(pack->len); ptr+= 2; + memcpy(ptr, RA, 16); ptr+=16; + + list_for_each_entry(attr, &pack->attrs, entry) { + if (attr->vendor) { + *ptr = 26; ptr++; + *ptr = attr->len + 2 + 6; ptr++; + *(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4; + } + *ptr = attr->attr->id; ptr++; + *ptr = attr->len + 2; ptr++; + switch(attr->attr->type) { + case ATTR_TYPE_INTEGER: + *(uint32_t*)ptr = htonl(attr->val.integer); + break; + case ATTR_TYPE_OCTETS: + case ATTR_TYPE_STRING: + memcpy(ptr, attr->val.string, attr->len); + break; + case ATTR_TYPE_IPADDR: + *(in_addr_t*)ptr = attr->val.ipaddr; + break; + case ATTR_TYPE_DATE: + *(uint32_t*)ptr = htonl(attr->val.date); + break; + default: + log_emerg("radius:packet:BUG: unknown attribute type\n"); + abort(); + } + ptr += attr->len; + } + + //print_buf(pack->buf, pack->len); + return 0; +} + +int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr) +{ + struct rad_packet_t *pack; + struct rad_attr_t *attr; + struct rad_dict_attr_t *da; + struct rad_dict_vendor_t *vendor; + uint8_t *ptr; + int n, id, len, vendor_id; + socklen_t addr_len = sizeof(*addr); + + *p = NULL; + + pack = rad_packet_alloc(0); + if (!pack) + return 0; + + pack->buf = _malloc(REQ_LENGTH_MAX); + if (!pack->buf) { + log_emerg("radius:packet: out of memory\n"); + goto out_err; + } + + while (1) { + if (addr) + n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len); + else + n = read(fd, pack->buf, REQ_LENGTH_MAX); + if (n < 0) { + if (errno == EAGAIN) { + rad_packet_free(pack); + return -1; + } + if (errno != ECONNREFUSED) + log_ppp_error("radius:packet:read: %s\n", strerror(errno)); + goto out_err; + } + break; + } + + if (n < 20) { + log_ppp_warn("radius:packet: short packed received (%i)\n", n); + goto out_err; + } + + ptr = (uint8_t *)pack->buf; + + pack->code = *ptr; ptr++; + pack->id = *ptr; ptr++; + pack->len = ntohs(*(uint16_t*)ptr); ptr += 2; + + if (pack->len > n) { + log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n); + goto out_err; + } + + ptr += 16; + n -= 20; + + while (n>0) { + id = *ptr; ptr++; + len = *ptr - 2; ptr++; + if (len < 0) { + log_ppp_warn("radius:packet short attribute len received\n"); + goto out_err; + } + if (2 + len > n) { + log_ppp_warn("radius:packet: too long attribute received (%i, %i)\n", id, len); + goto out_err; + } + 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++; + n -= 2 + 4; + } else + log_ppp_warn("radius:packet: vendor %i not found\n", id); + } else + vendor = NULL; + da = rad_dict_find_attr_id(vendor, id); + if (da) { + attr = mempool_alloc(attr_pool); + if (!attr) { + log_emerg("radius:packet: out of memory\n"); + goto out_err; + } + memset(attr, 0, sizeof(*attr)); + attr->vendor = vendor; + attr->attr = da; + attr->len = len; + switch (da->type) { + case ATTR_TYPE_STRING: + attr->val.string = _malloc(len+1); + if (!attr->val.string) { + log_emerg("radius:packet: out of memory\n"); + _free(attr); + goto out_err; + } + memcpy(attr->val.string, ptr, len); + attr->val.string[len] = 0; + break; + case ATTR_TYPE_OCTETS: + attr->val.octets = _malloc(len); + if (!attr->val.octets) { + log_emerg("radius:packet: out of memory\n"); + _free(attr); + goto out_err; + } + memcpy(attr->val.octets, ptr, len); + break; + 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; + } + list_add_tail(&attr->entry, &pack->attrs); + } else + log_ppp_warn("radius:packet: unknown attribute received (%i,%i)\n", vendor ? vendor->id : 0, id); + ptr += len; + n -= 2 + len; + } + + *p = pack; + + return 0; + +out_err: + rad_packet_free(pack); + return 0; +} + +void rad_packet_free(struct rad_packet_t *pack) +{ + struct rad_attr_t *attr; + + if (pack->buf) + _free(pack->buf); + + while(!list_empty(&pack->attrs)) { + attr = list_entry(pack->attrs.next, typeof(*attr), entry); + list_del(&attr->entry); + if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS) + _free(attr->val.string); + mempool_free(attr); + } + + mempool_free(pack); +} + +void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...)) +{ + struct rad_attr_t *attr; + 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; + case CODE_ACCOUNTING_REQUEST: + print("Accounting-Request"); + break; + case CODE_ACCOUNTING_RESPONSE: + print("Accounting-Response"); + break; + case CODE_DISCONNECT_REQUEST: + print("Disconnect-Request"); + break; + case CODE_DISCONNECT_ACK: + print("Disconnect-ACK"); + break; + case CODE_DISCONNECT_NAK: + print("Disconnect-NAK"); + break; + case CODE_COA_REQUEST: + print("CoA-Request"); + break; + case CODE_COA_ACK: + print("CoA-ACK"); + break; + case CODE_COA_NAK: + print("CoA-NAK"); + break; + default: + print("Unknown (%i)", pack->code); + } + print(" id=%x", pack->id); + + list_for_each_entry(attr, &pack->attrs, entry) { + if (attr->vendor) + print("<%s %s ", attr->vendor->name, attr->attr->name); + else + print(" <%s ", attr->attr->name); + 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("%u", 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; + } + print(">"); + } + print("]\n"); +} + +int __export rad_packet_add_int(struct rad_packet_t *pack, const char *vendor_name, const char *name, int val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + + if (pack->len + (vendor_name ? 8 : 2) + 4 >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) + return -1; + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = 4; + ra->val.integer = val; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + 4; + + return 0; +} + +int __export rad_packet_change_int(struct rad_packet_t *pack, const char *vendor_name, const char *name, int val) +{ + struct rad_attr_t *ra; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + ra->val.integer = val; + + return 0; +} + +int __export rad_packet_add_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 + (vendor_name ? 8 : 2) + len >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + 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; + ra->val.octets = _malloc(len); + if (!ra->val.octets) { + log_emerg("radius: out of memory\n"); + _free(ra); + return -1; + } + memcpy(ra->val.octets, val, len); + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + len; + + return 0; +} + +int __export rad_packet_change_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_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; +} + + +int __export rad_packet_add_str(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + int len = strlen(val); + + if (pack->len + (vendor_name ? 8 : 2) + len >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + 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; + ra->val.string = _malloc(len + 1); + if (!ra->val.string) { + log_emerg("radius: out of memory\n"); + _free(ra); + return -1; + } + memcpy(ra->val.string, val, len); + ra->val.string[len] = 0; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + len; + + return 0; +} + +int __export rad_packet_change_str(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val, int len) +{ + struct rad_attr_t *ra; + + ra = rad_packet_find_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.string = _realloc(ra->val.string, len + 1); + if (!ra->val.string) { + log_emerg("radius: out of memory\n"); + return -1; + } + + pack->len += len - ra->len; + ra->len = len; + } + + memcpy(ra->val.string, val, len); + ra->val.string[len] = 0; + + return 0; +} + +int __export rad_packet_add_val(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_value_t *v; + struct rad_dict_vendor_t *vendor; + + if (pack->len + (vendor_name ? 8 : 2) + 4 >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + v = rad_dict_find_val_name(attr, val); + if (!v) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) + return -1; + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = 4; + ra->val = v->val; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + 4; + + return 0; +} + +int __export rad_packet_change_val(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_value_t *v; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + v = rad_dict_find_val_name(ra->attr, val); + if (!v) + return -1; + + ra->val = v->val; + + return 0; +} + +int __export rad_packet_add_ipaddr(struct rad_packet_t *pack, const char *vendor_name, const char *name, in_addr_t ipaddr) +{ + return rad_packet_add_int(pack, vendor_name, name, ipaddr); +} + + +struct rad_attr_t __export *rad_packet_find_attr(struct rad_packet_t *pack, const char *vendor_name, const char *name) +{ + struct rad_attr_t *ra; + struct rad_dict_vendor_t *vendor; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return NULL; + } else + vendor = NULL; + + list_for_each_entry(ra, &pack->attrs, entry) { + if (vendor && vendor != ra->vendor) + continue; + + if (strcmp(ra->attr->name, name)) + continue; + + 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; + log_ppp_error("radius:write: %s\n", strerror(errno)); + return -1; + } else if (n != pack->len) { + log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len); + return -1; + } + break; + } + + return 0; +} + +static void __init init(void) +{ + attr_pool = mempool_create(sizeof(struct rad_attr_t)); + packet_pool = mempool_create(sizeof(struct rad_packet_t)); +} |