static void got_packet(UNUSED uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data) { static int count = 1; /* Packets seen */ /* * Define pointers for packet's attributes */ const struct ip_header *ip; /* The IP header */ const struct udp_header *udp; /* The UDP header */ const uint8_t *payload; /* Packet payload */ /* * And define the size of the structures we're using */ int size_ethernet = sizeof(struct ethernet_header); int size_ip = sizeof(struct ip_header); int size_udp = sizeof(struct udp_header); /* * For FreeRADIUS */ RADIUS_PACKET *packet, *original; struct timeval elapsed; /* * Define our packet's attributes */ if ((data[0] == 2) && (data[1] == 0) && (data[2] == 0) && (data[3] == 0)) { ip = (const struct ip_header*) (data + 4); } else { ip = (const struct ip_header*)(data + size_ethernet); } udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip); payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp); packet = rad_alloc(NULL, 0); if (!packet) { fprintf(stderr, "Out of memory\n"); return; } packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; packet->src_port = ntohs(udp->udp_sport); packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; packet->dst_port = ntohs(udp->udp_dport); memcpy(&packet->data, &payload, sizeof(packet->data)); packet->data_len = header->len - (payload - data); if (!rad_packet_ok(packet, 0)) { DEBUG(log_dst, "Packet: %s\n", fr_strerror()); DEBUG(log_dst, " From %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); DEBUG(log_dst, " To: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG(log_dst, " Type: %s\n", fr_packet_codes[packet->code]); rad_free(&packet); return; } switch (packet->code) { case PW_COA_REQUEST: /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */ original = nullpacket; break; case PW_AUTHENTICATION_ACK: /* look for a matching request and use it for decoding */ original = rbtree_finddata(request_tree, packet); break; case PW_AUTHENTICATION_REQUEST: /* save the request for later matching */ original = rad_alloc_reply(NULL, packet); if (original) { /* just ignore allocation failures */ rbtree_deletebydata(request_tree, original); rbtree_insert(request_tree, original); } /* fallthrough */ default: /* don't attempt to decode any encrypted attributes */ original = NULL; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(packet, original, radius_secret) != 0) { rad_free(&packet); fr_perror("decode"); return; } /* * We've seen a successfull reply to this, so delete it now */ if (original) rbtree_deletebydata(request_tree, original); if (filter_vps && filter_packet(packet)) { rad_free(&packet); DEBUG(log_dst, "Packet number %d doesn't match\n", count++); return; } if (out) { pcap_dump((void *) out, header, data); goto check_filter; } INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id); /* * Print the RADIUS packet */ INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); DEBUG1(log_dst, "\t(%d packets)", count++); if (!start_pcap.tv_sec) { start_pcap = header->ts; } tv_sub(&header->ts, &start_pcap, &elapsed); INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec, (unsigned int) elapsed.tv_usec / 1000); if (fr_debug_flag > 1) { DEBUG(log_dst, "\n"); if (packet->vps) { if (do_sort) sort(packet); vp_printlist(log_dst, packet->vps); pairfree(&packet->vps); } } INFO(log_dst, "\n"); if (!to_stdout && (fr_debug_flag > 4)) { rad_print_hex(packet); } fflush(log_dst); check_filter: /* * If we're doing filtering, Access-Requests are cached in the * filter tree. */ if (!filter_vps || ((packet->code != PW_AUTHENTICATION_REQUEST) && (packet->code != PW_ACCOUNTING_REQUEST))) { rad_free(&packet); } }
int main(int argc, char *argv[]) { const char *from_dev = NULL; /* Capture from device */ const char *from_file = NULL; /* Read from pcap file */ int from_stdin = 0; /* Read from stdin */ pcap_t *in; /* PCAP input handle */ int limit = -1; /* How many packets to sniff */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ char *to_file = NULL; /* PCAP output file */ char *pcap_filter = NULL; /* PCAP filter string */ char *radius_filter = NULL; int port = 1812; struct bpf_program fp; /* Holds compiled filter */ bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN; /* Device Subnet mask */ bpf_u_int32 ip_addr = 0; /* Device IP */ char buffer[1024]; int opt; FR_TOKEN parsecode; const char *radius_dir = RADIUS_DIR; fr_debug_flag = 2; log_dst = stdout; /* * Get options */ while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) { switch (opt) { case 'c': limit = atoi(optarg); if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'F': from_stdin = 1; to_stdout = 1; break; case 'f': pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': from_dev = optarg; break; case 'I': from_file = optarg; break; case 'p': port = atoi(optarg); break; case 'q': if (fr_debug_flag > 0) { fr_debug_flag--; } break; case 'r': radius_filter = optarg; break; case 's': radius_secret = optarg; break; case 'S': do_sort = 1; break; case 'v': INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version()); exit(0); break; case 'w': to_file = optarg; break; case 'x': case 'X': fr_debug_flag++; break; default: usage(64); } } /* What's the point in specifying -F ?! */ if (from_stdin && from_file && to_file) { usage(64); } /* Can't read from both... */ if (from_file && from_dev) { usage(64); } /* Reading from file overrides stdin */ if (from_stdin && (from_file || from_dev)) { from_stdin = 0; } /* Writing to file overrides stdout */ if (to_file && to_stdout) { to_stdout = 0; } /* * If were writing pcap data stdout we *really* don't want to send * logging there as well. */ log_dst = to_stdout ? stderr : stdout; #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) if (from_stdin || to_stdout) { fprintf(stderr, "radsniff: PCAP streams not supported.\n"); exit(64); } #endif if (!pcap_filter) { pcap_filter = buffer; snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, 3799); } /* * There are times when we don't need the dictionaries. */ if (!to_stdout) { if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); exit(64); } } if (radius_filter) { parsecode = userparse(radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror()); exit(64); } if (!filter_vps) { fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter); exit(64); } filter_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!filter_tree) { fprintf(stderr, "radsniff: Failed creating filter tree\n"); exit(1); } } /* * Setup the request tree */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!request_tree) { fprintf(stderr, "radsniff: Failed creating request tree\n"); exit(1); } /* * Allocate a null packet for decrypting attributes in CoA requests */ nullpacket = rad_alloc(NULL, 0); if (!nullpacket) { fprintf(stderr, "radsniff: Out of memory\n"); exit(1); } /* * Get the default capture device */ if (!from_stdin && !from_file && !from_dev) { from_dev = pcap_lookupdev(errbuf); if (!from_dev) { fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf); exit(1); } INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev); } /* * Print captures values which will be used */ if (fr_debug_flag > 2) { DEBUG1(log_dst, "Sniffing with options:\n"); if (from_dev) DEBUG1(log_dst, " Device : [%s]\n", from_dev); if (limit > 0) DEBUG1(log_dst, " Capture limit (packets) : [%d]\n", limit); DEBUG1(log_dst, " PCAP filter : [%s]\n", pcap_filter); DEBUG1(log_dst, " RADIUS secret : [%s]\n", radius_secret); if (filter_vps){DEBUG1(log_dst, " RADIUS filter :\n"); vp_printlist(log_dst, filter_vps); } } /* * Figure out whether were doing a reading from a file, doing a live * capture or reading from stdin. */ if (from_file) { in = pcap_open_offline(from_file, errbuf); #ifdef HAVE_PCAP_FOPEN_OFFLINE } else if (from_stdin) { in = pcap_fopen_offline(stdin, errbuf); #endif } else if (from_dev) { pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf); in = pcap_open_live(from_dev, 65536, 1, 1, errbuf); } else { fprintf(stderr, "radsniff: No capture devices available\n"); } if (!in) { fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf); exit(1); } if (to_file) { out = pcap_dump_open(in, to_file); if (!out) { fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in)); exit(1); } #ifdef HAVE_PCAP_DUMP_FOPEN } else if (to_stdout) { out = pcap_dump_fopen(in, stdout); if (!out) { fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in)); exit(1); } #endif } /* * Apply the rules */ if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) { fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } if (pcap_setfilter(in, &fp) < 0) { fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } /* * Enter the main capture loop... */ pcap_loop(in, limit, got_packet, NULL); /* * ...were done capturing. */ pcap_close(in); if (out) { pcap_dump_close(out); } if (filter_tree) { rbtree_free(filter_tree); } INFO(log_dst, "Done sniffing\n"); return 0; }
static void rs_process_packet(rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data) { static int count = 0; /* Packets seen */ rs_stats_t *stats = event->stats; decode_fail_t reason; /* * Pointers into the packet data we just received */ size_t len; uint8_t const *p = data; struct ip_header const *ip = NULL; /* The IP header */ struct ip_header6 const *ip6 = NULL; /* The IPv6 header */ struct udp_header const *udp; /* The UDP header */ uint8_t version; /* IP header version */ bool response = false; /* Was it a response code */ RADIUS_PACKET *current, *original; struct timeval elapsed; struct timeval latency; count++; if (header->caplen <= 5) { INFO("Packet too small, captured %i bytes", header->caplen); return; } /* * Loopback header */ if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) { p += 4; /* * Ethernet header */ } else { p += sizeof(struct ethernet_header); } version = (p[0] & 0xf0) >> 4; switch (version) { case 4: ip = (struct ip_header const *)p; len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */ p += len; break; case 6: ip6 = (struct ip_header6 const *)p; p += sizeof(struct ip_header6); break; default: DEBUG("IP version invalid %i", version); return; } /* * End of variable length bits, do basic check now to see if packet looks long enough */ len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1); /* length value */ if (len > header->caplen) { DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes", (size_t) len, header->caplen); return; } udp = (struct udp_header const *)p; p += sizeof(struct udp_header); /* * With artificial talloc memory limits there's a good chance we can * recover once some requests timeout, so make an effort to deal * with allocation failures gracefully. */ current = rad_alloc(NULL, 0); if (!current) { ERROR("Failed allocating memory to hold decoded packet"); return; } current->timestamp = header->ts; current->data_len = header->caplen - (data - p); memcpy(¤t->data, &p, sizeof(current->data)); /* * Populate IP/UDP fields from PCAP data */ if (ip) { current->src_ipaddr.af = AF_INET; current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; current->dst_ipaddr.af = AF_INET; current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; } else { current->src_ipaddr.af = AF_INET6; memcpy(¤t->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr, sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr)); current->dst_ipaddr.af = AF_INET6; memcpy(¤t->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr, sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr)); } current->src_port = ntohs(udp->udp_sport); current->dst_port = ntohs(udp->udp_dport); if (!rad_packet_ok(current, 0, &reason)) { DEBUG("(%i) ** %s **", count, fr_strerror()); DEBUG("(%i) %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); rad_free(¤t); return; } switch (current->code) { case PW_CODE_COA_REQUEST: /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */ original = nullpacket; break; case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_AUTHENTICATION_REJECT: case PW_CODE_AUTHENTICATION_ACK: response = true; /* look for a matching request and use it for decoding */ original = rbtree_finddata(request_tree, current); break; case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_AUTHENTICATION_REQUEST: /* save the request for later matching */ original = rad_alloc_reply(event->conf, current); original->timestamp = header->ts; if (original) { /* just ignore allocation failures */ rbtree_deletebydata(request_tree, original); rbtree_insert(request_tree, original); } /* fallthrough */ default: /* don't attempt to decode any encrypted attributes */ original = NULL; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(current, original, event->conf->radius_secret) != 0) { rad_free(¤t); fr_perror("decode"); return; } if (filter_vps && rs_filter_packet(current)) { rad_free(¤t); DEBUG("Packet number %d doesn't match", count++); return; } if (event->out) { pcap_dump((void *) (event->out->dumper), header, data); goto check_filter; } rs_tv_sub(&header->ts, &start_pcap, &elapsed); rs_stats_update_count(&stats->gauge, current); if (original) { rs_tv_sub(¤t->timestamp, &original->timestamp, &latency); /* * Update stats for both the request and response types. * * This isn't useful for things like Access-Requests, but will be useful for * CoA and Disconnect Messages, as we get the average latency across both * response types. * * It also justifies allocating 255 instances rs_latency_t. */ rs_stats_update_latency(&stats->exchange[current->code], &latency); rs_stats_update_latency(&stats->exchange[original->code], &latency); /* * Print info about the response. */ DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000), (unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000)); /* * It's the original request */ } else { /* * Print info about the request */ DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); } if (fr_debug_flag > 1) { if (current->vps) { if (event->conf->do_sort) { pairsort(¤t->vps, true); } vp_printlist(log_dst, current->vps); pairfree(¤t->vps); } } /* * We've seen a successful reply to this, so delete it now */ if (original) { rbtree_deletebydata(request_tree, original); } if (!event->conf->to_stdout && (fr_debug_flag > 4)) { rad_print_hex(current); } fflush(log_dst); check_filter: /* * If we're doing filtering, Access-Requests are cached in the * filter tree. */ if (!filter_vps || ((current->code != PW_CODE_AUTHENTICATION_REQUEST) && (current->code != PW_CODE_ACCOUNTING_REQUEST))) { rad_free(¤t); } }
int main(int argc, char *argv[]) { rs_t *conf; fr_pcap_t *in = NULL, *in_p; fr_pcap_t **in_head = ∈ fr_pcap_t *out = NULL; int ret = 1; /* Exit status */ int limit = -1; /* How many packets to sniff */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ int port = 1812; char buffer[1024]; int opt; FR_TOKEN parsecode; char const *radius_dir = RADIUS_DIR; rs_stats_t stats; fr_debug_flag = 2; log_dst = stdout; talloc_set_log_stderr(); conf = talloc_zero(NULL, rs_t); if (!fr_assert(conf)) { exit (1); } /* * We don't really want probes taking down machines */ #ifdef HAVE_TALLOC_SET_MEMLIMIT talloc_set_memlimit(conf, 52428800); /* 50 MB */ #endif /* * Get options */ while ((opt = getopt(argc, argv, "c:d:DFf:hi:I:p:qr:s:Svw:xXW:P:O:")) != EOF) { switch (opt) { case 'c': limit = atoi(optarg); if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'D': { pcap_if_t *all_devices = NULL; pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } int i = 1; for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { INFO("%i.%s", i++, dev_p->name); } ret = 0; goto finish; } case 'F': conf->from_stdin = true; conf->to_stdout = true; break; case 'f': conf->pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_dev = true; break; case 'I': *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_file = true; break; case 'p': port = atoi(optarg); break; case 'q': if (fr_debug_flag > 0) { fr_debug_flag--; } break; case 'r': conf->radius_filter = optarg; break; case 's': conf->radius_secret = optarg; break; case 'S': conf->do_sort = true; break; case 'v': #ifdef HAVE_COLLECTDC_H INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(), lcc_version_string()); #else INFO("%s %s", radsniff_version, pcap_lib_version()); #endif exit(0); break; case 'w': out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT); conf->to_file = true; break; case 'x': case 'X': fr_debug_flag++; break; case 'W': conf->stats.interval = atoi(optarg); if (conf->stats.interval <= 0) { ERROR("Stats interval must be > 0"); usage(64); } break; case 'T': conf->stats.timeout = atoi(optarg); if (conf->stats.timeout <= 0) { ERROR("Timeout value must be > 0"); usage(64); } break; #ifdef HAVE_COLLECTDC_H case 'P': conf->stats.prefix = optarg; break; case 'O': conf->stats.collectd = optarg; conf->stats.out = RS_STATS_OUT_COLLECTD; break; #endif default: usage(64); } } /* What's the point in specifying -F ?! */ if (conf->from_stdin && conf->from_file && conf->to_file) { usage(64); } /* Can't read from both... */ if (conf->from_file && conf->from_dev) { usage(64); } /* Reading from file overrides stdin */ if (conf->from_stdin && (conf->from_file || conf->from_dev)) { conf->from_stdin = false; } /* Writing to file overrides stdout */ if (conf->to_file && conf->to_stdout) { conf->to_stdout = false; } if (conf->to_stdout) { out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT); if (!out) { goto finish; } } if (conf->from_stdin) { *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; } if (!conf->radius_secret) { conf->radius_secret = RS_DEFAULT_SECRET; } if (conf->stats.interval && !conf->stats.out) { conf->stats.out = RS_STATS_OUT_STDIO; } if (conf->stats.timeout == 0) { conf->stats.timeout = RS_DEFAULT_TIMEOUT; } /* * If were writing pcap data stdout we *really* don't want to send * logging there as well. */ log_dst = conf->to_stdout ? stderr : stdout; #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) if (conf->from_stdin || conf->to_stdout) { ERROR("PCAP streams not supported"); goto finish; } #endif if (!conf->pcap_filter) { snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, 3799); conf->pcap_filter = buffer; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); ret = 64; goto finish; } fr_strerror(); /* Clear out any non-fatal errors */ if (conf->radius_filter) { parsecode = userparse(NULL, conf->radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { ERROR("Invalid RADIUS filter \"%s\" (%s)", conf->radius_filter, fr_strerror()); ret = 64; goto finish; } if (!filter_vps) { ERROR("Empty RADIUS filter \"%s\"", conf->radius_filter); ret = 64; goto finish; } filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!filter_tree) { ERROR("Failed creating filter tree"); ret = 64; goto finish; } } /* * Setup the request tree */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!request_tree) { ERROR("Failed creating request tree"); goto finish; } /* * Allocate a null packet for decrypting attributes in CoA requests */ nullpacket = rad_alloc(conf, 0); if (!nullpacket) { ERROR("Out of memory"); goto finish; } /* * Get the default capture device */ if (!conf->from_stdin && !conf->from_file && !conf->from_dev) { pcap_if_t *all_devices; /* List of all devices libpcap can listen on */ pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } if (!all_devices) { ERROR("No capture files specified and no live interfaces available"); ret = 64; goto finish; } for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { /* Don't use the any devices, it's horribly broken */ if (!strcmp(dev_p->name, "any")) continue; *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN); in_head = &(*in_head)->next; } conf->from_auto = true; conf->from_dev = true; INFO("Defaulting to capture on all interfaces"); } /* * Print captures values which will be used */ if (fr_debug_flag > 2) { DEBUG1("Sniffing with options:"); if (conf->from_dev) { char *buff = fr_pcap_device_names(conf, in, ' '); DEBUG1(" Device(s) : [%s]", buff); talloc_free(buff); } if (conf->to_file || conf->to_stdout) { DEBUG1(" Writing to : [%s]", out->name); } if (limit > 0) { DEBUG1(" Capture limit (packets) : [%d]", limit); } DEBUG1(" PCAP filter : [%s]", conf->pcap_filter); DEBUG1(" RADIUS secret : [%s]", conf->radius_secret); if (filter_vps){ DEBUG1(" RADIUS filter :"); vp_printlist(log_dst, filter_vps); } } /* * Open our interface to collectd */ #ifdef HAVE_COLLECTDC_H if (conf->stats.out == RS_STATS_OUT_COLLECTD) { size_t i; rs_stats_tmpl_t *tmpl, **next; if (rs_stats_collectd_open(conf) < 0) { exit(1); } next = &conf->stats.tmpl; for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) { tmpl = rs_stats_collectd_init_latency(conf, next, conf, "radius_pkt_ex", &stats.exchange[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { goto tmpl_error; } next = &(tmpl->next); tmpl = rs_stats_collectd_init_counter(conf, next, conf, "radius_pkt", &stats.gauge.type[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { tmpl_error: ERROR("Error allocating memory for stats template"); goto finish; } next = &(tmpl->next); } } #endif /* * This actually opens the capture interfaces/files (we just allocated the memory earlier) */ { fr_pcap_t *prev = NULL; for (in_p = in; in_p; in_p = in_p->next) { if (fr_pcap_open(in_p) < 0) { if (!conf->from_auto) { ERROR("Failed opening pcap handle for %s", in_p->name); goto finish; } DEBUG("Failed opening pcap handle: %s", fr_strerror()); /* Unlink it from the list */ if (prev) { prev->next = in_p->next; talloc_free(in_p); in_p = prev; } else { in = in_p->next; talloc_free(in_p); in_p = in; } goto next; } if (conf->pcap_filter) { if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) { ERROR("Failed applying filter"); goto finish; } } next: prev = in_p; } } /* * Open our output interface (if we have one); */ if (out) { if (fr_pcap_open(out) < 0) { ERROR("Failed opening pcap output"); goto finish; } } /* * Setup and enter the main event loop. Who needs libev when you can roll your own... */ { struct timeval now; fr_event_list_t *events; rs_update_t update; char *buff; memset(&stats, 0, sizeof(stats)); memset(&update, 0, sizeof(update)); events = fr_event_list_create(conf, _rs_event_status); if (!events) { ERROR(); goto finish; } for (in_p = in; in_p; in_p = in_p->next) { rs_event_t *event; event = talloc_zero(events, rs_event_t); event->conf = conf; event->in = in_p; event->out = out; event->stats = &stats; if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) { ERROR("Failed inserting file descriptor"); goto finish; } } buff = fr_pcap_device_names(conf, in, ' '); INFO("Sniffing on (%s)", buff); talloc_free(buff); gettimeofday(&now, NULL); start_pcap = now; /* * Insert our stats processor */ if (conf->stats.interval) { update.list = events; update.conf = conf; update.stats = &stats; update.in = in; now.tv_sec += conf->stats.interval; now.tv_usec = 0; fr_event_insert(events, rs_stats_process, (void *) &update, &now, NULL); } ret = fr_event_loop(events); /* Enter the main event loop */ } INFO("Done sniffing"); finish: if (filter_tree) { rbtree_free(filter_tree); } INFO("Exiting..."); /* * Free all the things! This also closes all the sockets and file descriptors */ talloc_free(conf); return ret; }
/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; radclient_t *radclient; RADIUS_PACKET *reply, **request_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ if (wait_time <= 0) { tv.tv_sec = 0; } else { tv.tv_sec = wait_time; } tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) { return 0; } /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { fprintf(stderr, "radclient: received bad packet: %s\n", fr_strerror()); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * udpfromto issues. We may have bound to "*", * and we want to find the replies that are sent to * (say) 127.0.0.1. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; #endif if (fr_debug_flag > 2) print_hex(reply); request_p = fr_packet_list_find_byreply(pl, reply); if (!request_p) { fprintf(stderr, "radclient: received response to request we did not send. (id=%d socket %d)\n", reply->id, reply->sockfd); rad_free(&reply); return -1; /* got reply to packet we didn't send */ } radclient = fr_packet2myptr(radclient_t, request, request_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (rad_verify(reply, radclient->request, secret) < 0) { fr_perror("rad_verify"); totallost++; goto packet_done; /* shared secret is incorrect */ } fr_packet_list_yank(pl, radclient->request); if (print_filename) printf("%s:%d %d\n", radclient->filename, radclient->packet_number, reply->code); deallocate_id(radclient); radclient->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (rad_decode(radclient->reply, radclient->request, secret) != 0) { fr_perror("rad_decode"); totallost++; goto packet_done; } /* libradius debug already prints out the value pairs for us */ if (!fr_debug_flag && do_output) { printf("Received response ID %d, code %d, length = %zd\n", radclient->reply->id, radclient->reply->code, radclient->reply->data_len); vp_printlist(stdout, radclient->reply->vps); } if ((radclient->reply->code == PW_AUTHENTICATION_ACK) || (radclient->reply->code == PW_ACCOUNTING_RESPONSE) || (radclient->reply->code == PW_COA_ACK) || (radclient->reply->code == PW_DISCONNECT_ACK)) { success = 1; /* have a good response */ totalapp++; } else { totaldeny++; } if (radclient->resend == resend_count) { radclient->done = 1; } packet_done: rad_free(&radclient->reply); rad_free(&reply); /* may be NULL */ return 0; }
main(int argc, char *argv[]) { int filedone; RADIUS_PACKET *req,*req2; VALUE_PAIR *vp, *vpkey, *vpextra; extern unsigned int sha1_data_problems; req = NULL; req2 = NULL; filedone = 0; if(argc>1) { sha1_data_problems = 1; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { librad_perror("radclient"); return 1; } if ((req = rad_alloc(1)) == NULL) { librad_perror("radclient"); exit(1); } if ((req2 = rad_alloc(1)) == NULL) { librad_perror("radclient"); exit(1); } while(!filedone) { if(req->vps) pairfree(&req->vps); if(req2->vps) pairfree(&req2->vps); if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) { break; } printf("\nRead:\n"); vp_printlist(stdout, req->vps); map_eapsim_types(req); map_eap_types(req); printf("Mapped to:\n"); vp_printlist(stdout, req->vps); /* find the EAP-Message, copy it to req2 */ vp = paircopy2(req->vps, PW_EAP_MESSAGE); if(vp == NULL) continue; pairadd(&req2->vps, vp); /* only call unmap for sim types here */ unmap_eap_types(req2); unmap_eapsim_types(req2); printf("Unmapped to:\n"); vp_printlist(stdout, req2->vps); vp = pairfind(req2->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC); vpkey = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY); vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA); if(vp != NULL && vpkey != NULL && vpextra!=NULL) { uint8_t calcmac[16]; /* find the EAP-Message, copy it to req2 */ memset(calcmac, 0, sizeof(calcmac)); printf("Confirming MAC..."); if(eapsim_checkmac(req2->vps, vpkey->strvalue, vpextra->strvalue, vpextra->length, calcmac)) { printf("succeed\n"); } else { int i, j; printf("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { printf("_"); j=0; } j++; printf("%02x", calcmac[i]); } printf(" did not match\n"); } } fflush(stdout); } }
main(int argc, char *argv[]) { int filedone; RADIUS_PACKET *req,*req2; VALUE_PAIR *vp, *vpkey, *vpextra; extern unsigned int sha1_data_problems; req = NULL; req2 = NULL; filedone = 0; if(argc>1) { sha1_data_problems = 1; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { ERROR("%s", fr_strerror()); return 1; } req = rad_alloc(NULL, true) if (!req) { ERROR("%s", fr_strerror()); exit(1); } req2 = rad_alloc(NULL, true); if (!req2) { ERROR("%s", fr_strerror()); exit(1); } while(!filedone) { if (req->vps) pairfree(&req->vps); if (req2->vps) pairfree(&req2->vps); if (readvp2(&req->vps, NULL, stdin, &filedone) < 0) { ERROR("%s", fr_strerror()); break; } if (fr_debug_flag > 1) { DEBUG("Read:"); vp_printlist(stdout, req->vps); } map_eapsim_types(req); map_eap_methods(req); if (fr_debug_flag > 1) { DEBUG("Mapped to:"); vp_printlist(stdout, req->vps); } /* find the EAP-Message, copy it to req2 */ vp = paircopy2(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if(!vp) continue; pairadd(&req2->vps, vp); /* only call unmap for sim types here */ unmap_eap_methods(req2); unmap_eapsim_types(req2); if (fr_debug_flag > 1) { DEBUG("Unmapped to:"); vp_printlist(stdout, req2->vps); } vp = pairfind(req2->vps, PW_EAP_SIM_MAC, 0, TAG_ANY); vpkey = pairfind(req->vps, PW_EAP_SIM_KEY, 0, TAG_ANY); vpextra = pairfind(req->vps, PW_EAP_SIM_EXTRA, 0, TAG_ANY); if(vp != NULL && vpkey != NULL && vpextra!=NULL) { uint8_t calcmac[16]; /* find the EAP-Message, copy it to req2 */ memset(calcmac, 0, sizeof(calcmac)); DEBUG("Confirming MAC..."); if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue, vpextra->vp_strvalue, vpextra->length, calcmac)) { DEBUG("succeed"); } else { int i, j; DEBUG("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { DEBUG("_"); j=0; } j++; DEBUG("%02x", calcmac[i]); } DEBUG("did not match"); } } fflush(stdout); } }
static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep) { int i; struct timeval tv; for (i = 0; i < retries; i++) { fd_set rdfdesc; rad_send(req, NULL, secret); /* And wait for reply, timing out as necessary */ FD_ZERO(&rdfdesc); FD_SET(req->sockfd, &rdfdesc); tv.tv_sec = (int)timeout; tv.tv_usec = 1000000 * (timeout - (int) timeout); /* Something's wrong if we don't get exactly one fd. */ if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) { continue; } *rep = rad_recv(req->sockfd); if (*rep != NULL) { /* * If we get a response from a machine * which we did NOT send a request to, * then complain. */ if (((*rep)->src_ipaddr != req->dst_ipaddr) || ((*rep)->src_port != req->dst_port)) { char src[64], dst[64]; ip_ntoa(src, (*rep)->src_ipaddr); ip_ntoa(dst, req->dst_ipaddr); fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!", dst, req->dst_port, src, (*rep)->src_port); exit(1); } break; } else { /* NULL: couldn't receive the packet */ librad_perror("radclient:"); exit(1); } } /* No response or no data read (?) */ if (i == retries) { fprintf(stderr, "radclient: no response from server\n"); exit(1); } /* * FIXME: Discard the packet & listen for another. * * Hmm... we should really be using eapol_test, which does * a lot more than radeapclient. */ if (rad_verify(*rep, req, secret) != 0) { librad_perror("rad_verify"); exit(1); } if (rad_decode(*rep, req, secret) != 0) { librad_perror("rad_decode"); exit(1); } /* libradius debug already prints out the value pairs for us */ if (!librad_debug && do_output) { printf("Received response ID %d, code %d, length = %d\n", (*rep)->id, (*rep)->code, (*rep)->data_len); vp_printlist(stdout, (*rep)->vps); } if((*rep)->code == PW_AUTHENTICATION_ACK) { totalapp++; } else { totaldeny++; } return 0; }
static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data) { /* Just a counter of how many packets we've had */ static int count = 1; /* Define pointers for packet's attributes */ const struct ethernet_header *ethernet; /* The ethernet header */ const struct ip_header *ip; /* The IP header */ const struct udp_header *udp; /* The UDP header */ const uint8_t *payload; /* Packet payload */ /* And define the size of the structures we're using */ int size_ethernet = sizeof(struct ethernet_header); int size_ip = sizeof(struct ip_header); int size_udp = sizeof(struct udp_header); /* For FreeRADIUS */ RADIUS_PACKET *packet; args = args; /* -Wunused */ /* Define our packet's attributes */ ethernet = (const struct ethernet_header*)(data); ip = (const struct ip_header*)(ethernet + size_ethernet); udp = (const struct udp_header*)(data + size_ethernet + size_ip); payload = (const uint8_t *)(data + size_ethernet + size_ip + size_udp); packet = malloc(sizeof(*packet)); if (!packet) { fprintf(stderr, "Out of memory\n"); return; } memset(packet, 0, sizeof(*packet)); packet->src_ipaddr.af = AF_INET; packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; packet->src_port = ntohs(udp->udp_sport); packet->dst_ipaddr.af = AF_INET; packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; packet->dst_port = ntohs(udp->udp_dport); packet->data = payload; packet->data_len = header->len - size_ethernet - size_ip - size_udp; if (!rad_packet_ok(packet, 0)) { fr_perror("Packet"); free(packet); return; } /* * Decode the data without bothering to check the signatures. */ if (rad_decode(packet, NULL, radius_secret) != 0) { free(packet); fr_perror("decode"); return; } if (filter_vps && filter_packet(packet)) { free(packet); DEBUG("Packet number %d doesn't match\n", count++); return; } /* Print the RADIUS packet */ printf("Packet number %d has just been sniffed\n", count++); printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); printf("\tType: %s\n", packet_codes[packet->code]); if (packet->vps != NULL) { vp_printlist(stdout, packet->vps); pairfree(&packet->vps); } fflush(stdout); free(packet); }
int main(int argc, char *argv[]) { char *dev; /* sniffing device */ char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */ pcap_t *descr; /* sniff handler */ struct bpf_program fp; /* hold compiled program */ bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ char buffer[1024]; char *pcap_filter = NULL; char *radius_filter = NULL; int packet_count = -1; /* how many packets to sniff */ int opt; FR_TOKEN parsecode; const char *radius_dir = RADIUS_DIR; int port = 1812; /* Default device */ dev = pcap_lookupdev(errbuf); /* Get options */ while ((opt = getopt(argc, argv, "c:d:f:hi:p:r:s:X")) != EOF) { switch (opt) { case 'c': packet_count = atoi(optarg); if (packet_count <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'f': pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': dev = optarg; break; case 'p': port = atoi(optarg); break; case 'r': radius_filter = optarg; break; case 's': radius_secret = optarg; break; case 'X': debug_flag = 1; break; default: usage(1); } } if (!pcap_filter) { pcap_filter = buffer; snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, port + 2); } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); return 1; } if (radius_filter) { parsecode = userparse(radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror()); exit(1); } if (!filter_vps) { fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter); exit(1); } } /* Set our device */ pcap_lookupnet(dev, &netp, &maskp, errbuf); /* Print device to the user */ printf("Device: [%s]\n", dev); if (packet_count > 0) { printf("Num of packets: [%d]\n", packet_count); } printf("PCAP filter: [%s]\n", pcap_filter); if (filter_vps != NULL) { printf("RADIUS filter:\n"); vp_printlist(stdout, filter_vps); } printf("RADIUS secret: [%s]\n", radius_secret); /* Open the device so we can spy */ descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf); if (descr == NULL) { printf("radsniff: pcap_open_live failed (%s)\n", errbuf); exit(1); } /* Apply the rules */ if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1) { printf("radsniff: pcap_compile failed\n"); exit(1); } if (pcap_setfilter(descr, &fp) == -1) { printf("radsniff: pcap_setfilter failed\n"); exit(1); } /* Now we can set our callback function */ pcap_loop(descr, packet_count, got_packet, NULL); pcap_close(descr); printf("Done sniffing\n"); fflush(stdout); return 0; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout; /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_socket_client_tcp(NULL, &request->packet->dst_ipaddr, request->packet->dst_port, false); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Failed opening socket"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &request->packet->dst_ipaddr, request->packet->dst_port, NULL)) { ERROR("Can't add new socket"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, request->password); } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { bool already_hex = false; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->vp_length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = true; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = strlen(request->password); if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, request->password, len); rad_chap_encode(request->packet, p, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->vp_length = 17; } } else if (pairfind(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); REDEBUG("No reply from server for ID %d socket %d", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); } fr_packet_header_print(fr_log_fp, request->packet, false); if (fr_debug_flag > 0) vp_printlist(fr_log_fp, request->packet->vps); return 0; }
static int sendrecv_eap(RADIUS_PACKET *rep) { RADIUS_PACKET *req = NULL; VALUE_PAIR *vp, *vpnext; int tried_eap_md5 = 0; /* * Keep a copy of the the User-Password attribute. */ if ((vp = pairfind(rep->vps, ATTRIBUTE_EAP_MD5_PASSWORD)) != NULL) { strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue)); } else if ((vp = pairfind(rep->vps, PW_PASSWORD)) != NULL) { strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue)); /* * Otherwise keep a copy of the CHAP-Password attribute. */ } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) { strNcpy(password, (char *)vp->strvalue, sizeof(vp->strvalue)); } else { *password = '******'; } again: rep->id++; printf("\n+++> About to send encoded packet:\n"); vp_printlist(stdout, rep->vps); /* * if there are EAP types, encode them into an EAP-Message * */ map_eap_types(rep); /* * Fix up Digest-Attributes issues */ for (vp = rep->vps; vp != NULL; vp = vp->next) { switch (vp->attribute) { default: break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ memmove(&vp->strvalue[2], &vp->strvalue[0], vp->length); vp->strvalue[0] = vp->attribute - PW_DIGEST_REALM + 1; vp->length += 2; vp->strvalue[1] = vp->length; vp->attribute = PW_DIGEST_ATTRIBUTES; break; } } /* * If we've already sent a packet, free up the old * one, and ensure that the next packet has a unique * ID and authentication vector. */ if (rep->data) { free(rep->data); rep->data = NULL; } librad_md5_calc(rep->vector, rep->vector, sizeof(rep->vector)); if (*password != '\0') { if ((vp = pairfind(rep->vps, PW_PASSWORD)) != NULL) { strNcpy((char *)vp->strvalue, password, strlen(password) + 1); vp->length = strlen(password); } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) { strNcpy((char *)vp->strvalue, password, strlen(password) + 1); vp->length = strlen(password); rad_chap_encode(rep, (char *) vp->strvalue, rep->id, vp); vp->length = 17; } } /* there WAS a password */ /* send the response, wait for the next request */ send_packet(rep, &req); /* okay got back the packet, go and decode the EAP-Message. */ unmap_eap_types(req); printf("<+++ EAP decoded packet:\n"); vp_printlist(stdout, req->vps); /* now look for the code type. */ for (vp = req->vps; vp != NULL; vp = vpnext) { vpnext = vp->next; switch (vp->attribute) { default: break; case ATTRIBUTE_EAP_BASE+PW_EAP_MD5: if(respond_eap_md5(req, rep) && tried_eap_md5 < 3) { tried_eap_md5++; goto again; } break; case ATTRIBUTE_EAP_BASE+PW_EAP_SIM: if(respond_eap_sim(req, rep)) { goto again; } break; } } return 1; }
/* * this code runs the EAP-SIM client state machine. * the *request* is from the server. * the *reponse* is to the server. * */ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp) { enum eapsim_clientstates state, newstate; enum eapsim_subtype subtype; VALUE_PAIR *vp, *statevp, *radstate, *eapid; char statenamebuf[32], subtypenamebuf[32]; if ((radstate = paircopy2(req->vps, PW_STATE)) == NULL) { return 0; } if ((eapid = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL) { return 0; } /* first, dig up the state from the request packet, setting * outselves to be in EAP-SIM-Start state if there is none. */ if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE)) == NULL) { /* must be initial request */ statevp = paircreate(ATTRIBUTE_EAP_SIM_STATE, PW_TYPE_INTEGER); statevp->lvalue = eapsim_client_init; pairreplace(&(resp->vps), statevp); } state = statevp->lvalue; /* * map the attributes, and authenticate them. */ unmap_eapsim_types(req); printf("<+++ EAP-sim decoded packet:\n"); vp_printlist(stdout, req->vps); if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL) { return 0; } subtype = vp->lvalue; /* * look for the appropriate state, and process incoming message */ switch(state) { case eapsim_client_init: switch(subtype) { case eapsim_start: newstate = process_eap_start(req, resp); break; case eapsim_challenge: case eapsim_notification: case eapsim_reauth: default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; case eapsim_client_start: switch(subtype) { case eapsim_start: /* NOT SURE ABOUT THIS ONE, retransmit, I guess */ newstate = process_eap_start(req, resp); break; case eapsim_challenge: newstate = process_eap_challenge(req, resp); break; default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; default: fprintf(stderr, "radeapclient: sim in illegal state %s\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf))); return 0; } /* copy the eap state object in */ pairreplace(&(resp->vps), eapid); /* update stete info, and send new packet */ map_eapsim_types(resp); /* copy the radius state object in */ pairreplace(&(resp->vps), radstate); statevp->lvalue = newstate; return 1; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout; /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_socket_client_tcp(NULL, &request->packet->dst_ipaddr, request->packet->dst_port, false); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Failed opening socket"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &request->packet->dst_ipaddr, request->packet->dst_port, NULL)) { ERROR("Can't add new socket"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password) { VALUE_PAIR *vp; if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { fr_pair_value_strcpy(vp, request->password->vp_strvalue); } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { uint8_t buffer[17]; rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password); fr_pair_value_memcpy(vp, buffer, 17); } else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); REDEBUG("No reply from server for ID %d socket %d", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); deallocate_id(request); request->done = true; return -1; } fr_packet_header_print(fr_log_fp, request->packet, false); if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps); return 0; }
static int do_packet(int allports, uint32_t nasaddr, const struct radutmp *u) { int i, retries=5, timeout=3; struct timeval tv; RADIUS_PACKET *req, *rep = NULL; VALUE_PAIR *vp; const char *secret; if ((req = rad_alloc(1)) == NULL) { librad_perror("radzap"); exit(1); } req->id = getpid() & 0xFF; req->code = PW_ACCOUNTING_REQUEST; req->dst_port = acct_port; if(req->dst_port == 0) req->dst_port = getport("radacct"); if(req->dst_port == 0) req->dst_port = PW_ACCT_UDP_PORT; if (radiusip == INADDR_NONE) { req->dst_ipaddr = ip_getaddr("localhost"); } else { req->dst_ipaddr = radiusip; } if(!req->dst_ipaddr) req->dst_ipaddr = 0x7f000001; req->vps = NULL; secret = getsecret(req->dst_ipaddr); if(allports != 0) { INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_ACCOUNTING_OFF); IPPAIR(PW_NAS_IP_ADDRESS, nasaddr); INTPAIR(PW_ACCT_DELAY_TIME, 0); } else { char login[sizeof u->login+1]; char session_id[sizeof u->session_id+1]; strNcpy(login, u->login, sizeof login); strNcpy(session_id, u->session_id, sizeof session_id); INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP); IPPAIR(PW_NAS_IP_ADDRESS, u->nas_address); INTPAIR(PW_ACCT_DELAY_TIME, 0); STRINGPAIR(PW_USER_NAME, login); INTPAIR(PW_NAS_PORT, u->nas_port); STRINGPAIR(PW_ACCT_SESSION_ID, session_id); if(u->proto=='P') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP); } else if(u->proto=='S') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP); } else { INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */ } IPPAIR(PW_FRAMED_IP_ADDRESS, u->framed_address); INTPAIR(PW_ACCT_SESSION_TIME, 0); INTPAIR(PW_ACCT_INPUT_OCTETS, 0); INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0); INTPAIR(PW_ACCT_INPUT_PACKETS, 0); INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0); } if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radzap: socket: "); exit(1); } for (i = 0; i < retries; i++) { fd_set rdfdesc; rad_send(req, NULL, secret); /* And wait for reply, timing out as necessary */ FD_ZERO(&rdfdesc); FD_SET(req->sockfd, &rdfdesc); tv.tv_sec = (int)timeout; tv.tv_usec = 1000000 * (timeout - (int)timeout); /* Something's wrong if we don't get exactly one fd. */ if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) { continue; } rep = rad_recv(req->sockfd); if (rep != NULL) { break; } else { /* NULL: couldn't receive the packet */ librad_perror("radzap:"); exit(1); } } /* No response or no data read (?) */ if (i == retries) { fprintf(stderr, "%s: no response from server\n", progname); exit(1); } if (rad_decode(rep, req, secret) != 0) { librad_perror("rad_decode"); exit(1); } vp_printlist(stdout, rep->vps); return 0; }
/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; rc_request_t *request; RADIUS_PACKET *reply, **packet_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ tv.tv_sec = (wait_time <= 0) ? 0 : wait_time; tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0; /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { ERROR("Received bad packet"); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * We don't use udpfromto. So if we bind to "*", we want * to find replies sent to 192.0.2.4. Therefore, we * force all replies to have the one address we know * about, no matter what real address they were sent to. * * This only works if were not using any of the * Packet-* attributes, or running with 'auto'. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP /* * TCP sockets don't use recvmsg(), and thus don't get * the source IP/port. However, since they're TCP, we * know what the source IP/port is, because that's where * we connected to. */ if (ipproto == IPPROTO_TCP) { reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; } #endif packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd); rad_free(&reply); return -1; /* got reply to packet we didn't send */ } request = fr_packet2myptr(rc_request_t, packet, packet_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (rad_verify(reply, request->packet, secret) < 0) { REDEBUG("Reply verification failed"); stats.lost++; goto packet_done; /* shared secret is incorrect */ } if (print_filename) { RDEBUG("%s response code %d", request->files->packets, reply->code); } deallocate_id(request); request->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (rad_decode(request->reply, request->packet, secret) != 0) { REDEBUG("Reply decode failed"); stats.lost++; goto packet_done; } fr_packet_header_print(fr_log_fp, request->reply, true); if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->reply->vps); /* * Increment counters... */ switch (request->reply->code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_ACK: stats.accepted++; break; case PW_CODE_ACCESS_CHALLENGE: break; default: stats.rejected++; } /* * If we had an expected response code, check to see if the * packet matched that. */ if (request->reply->code != request->filter_code) { if (is_radius_code(request->reply->code)) { REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code], fr_packet_codes[request->reply->code]); } else { REDEBUG("%s: Expected %u got %i", request->name, request->filter_code, request->reply->code); } stats.failed++; /* * Check if the contents of the packet matched the filter */ } else if (!request->filter) { stats.passed++; } else { VALUE_PAIR const *failed[2]; fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag); if (fr_pair_validate(failed, request->filter, request->reply->vps)) { RDEBUG("%s: Response passed filter", request->name); stats.passed++; } else { fr_pair_validate_debug(request, failed); REDEBUG("%s: Response for failed filter", request->name); stats.failed++; } } if (request->resend == resend_count) { request->done = true; } packet_done: rad_free(&request->reply); rad_free(&reply); /* may be NULL */ return 0; }
static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data) { rs_stats_t *stats = event->stats; struct timeval elapsed; struct timeval latency; /* * Pointers into the packet data we just received */ size_t len; uint8_t const *p = data; struct ip_header const *ip = NULL; /* The IP header */ struct ip_header6 const *ip6 = NULL; /* The IPv6 header */ struct udp_header const *udp; /* The UDP header */ uint8_t version; /* IP header version */ bool response; /* Was it a response code */ decode_fail_t reason; /* Why we failed decoding the packet */ static uint64_t captured = 0; RADIUS_PACKET *current; /* Current packet were processing */ rs_request_t *original; if (!start_pcap.tv_sec) { start_pcap = header->ts; } if (header->caplen <= 5) { INFO("Packet too small, captured %i bytes", header->caplen); return; } /* * Loopback header */ if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) { p += 4; /* * Ethernet header */ } else { p += sizeof(struct ethernet_header); } version = (p[0] & 0xf0) >> 4; switch (version) { case 4: ip = (struct ip_header const *)p; len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */ p += len; break; case 6: ip6 = (struct ip_header6 const *)p; p += sizeof(struct ip_header6); break; default: DEBUG("IP version invalid %i", version); return; } /* * End of variable length bits, do basic check now to see if packet looks long enough */ len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1); /* length value */ if (len > header->caplen) { DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes", (size_t) len, header->caplen); return; } udp = (struct udp_header const *)p; p += sizeof(struct udp_header); /* * With artificial talloc memory limits there's a good chance we can * recover once some requests timeout, so make an effort to deal * with allocation failures gracefully. */ current = rad_alloc(conf, 0); if (!current) { ERROR("Failed allocating memory to hold decoded packet"); rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet); return; } current->timestamp = header->ts; current->data_len = header->caplen - (p - data); memcpy(¤t->data, &p, sizeof(current->data)); /* * Populate IP/UDP fields from PCAP data */ if (ip) { current->src_ipaddr.af = AF_INET; current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr; current->dst_ipaddr.af = AF_INET; current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; } else { current->src_ipaddr.af = AF_INET6; memcpy(¤t->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr, sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr)); current->dst_ipaddr.af = AF_INET6; memcpy(¤t->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr, sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr)); } current->src_port = ntohs(udp->udp_sport); current->dst_port = ntohs(udp->udp_dport); if (!rad_packet_ok(current, 0, &reason)) { RIDEBUG("(%" PRIu64 ") ** %s **", count, fr_strerror()); RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); rad_free(¤t); return; } switch (current->code) { case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_AUTHENTICATION_REJECT: case PW_CODE_AUTHENTICATION_ACK: case PW_CODE_COA_NAK: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_NAK: case PW_CODE_DISCONNECT_ACK: case PW_CODE_STATUS_CLIENT: { rs_request_t search; struct timeval when; rs_tv_add_ms(&header->ts, conf->stats.timeout, &when); /* look for a matching request and use it for decoding */ search.packet = current; original = rbtree_finddata(request_tree, &search); /* * Only decode attributes if we want to print them or filter on them * rad_packet_ok does checks to verify the packet is actually valid. */ if (filter_vps || conf->print_packet) { if (rad_decode(current, original ? original->packet : NULL, conf->radius_secret) != 0) { rad_free(¤t); fr_perror("decode"); return; } } /* * Check if we've managed to link it to a request */ if (original) { /* * Is this a retransmit? */ if (!original->linked) { original->stats_rsp = &stats->exchange[current->code]; } else { RDEBUG("(%" PRIu64 ") ** RETRANSMISSION **", count); original->rt_rsp++; rad_free(&original->linked); fr_event_delete(event->list, &original->event); } original->linked = talloc_steal(original, current); /* * Some RADIUS servers and proxy servers may not cache * Accounting-Responses (and possibly other code), * and may immediately re-use a RADIUS packet src * port/id combination on receipt of a response. */ if (conf->dequeue[current->code]) { fr_event_delete(event->list, &original->event); rbtree_deletebydata(request_tree, original); } else { if (!fr_event_insert(event->list, rs_packet_cleanup, original, &when, &original->event)) { ERROR("Failed inserting new event"); /* * Delete the original request/event, it's no longer valid * for statistics. */ original->forced_cleanup = true; fr_event_delete(event->list, &original->event); rbtree_deletebydata(request_tree, original); return; } } /* * No request seen, or request was dropped by attribute filter */ } else { /* * If filter_vps are set assume the original request was dropped, * the alternative is maintaining another 'filter', but that adds * complexity, reduces max capture rate, and is generally a PITA. */ if (filter_vps) { rad_free(¤t); RDEBUG2("(%" PRIu64 ") Dropped by attribute filter", count); return; } RDEBUG("(%" PRIu64 ") ** UNLINKED **", count); stats->exchange[current->code].interval.unlinked_total++; } response = true; } break; case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_AUTHENTICATION_REQUEST: case PW_CODE_COA_REQUEST: case PW_CODE_DISCONNECT_REQUEST: case PW_CODE_STATUS_SERVER: { rs_request_t search; struct timeval when; /* * Only decode attributes if we want to print them or filter on them * rad_packet_ok does checks to verify the packet is actually valid. */ if (filter_vps || conf->print_packet) { if (rad_decode(current, NULL, conf->radius_secret) != 0) { rad_free(¤t); fr_perror("decode"); return; } } /* * Now verify the packet passes the attribute filter */ if (filter_vps && !pairvalidate_relaxed(filter_vps, current->vps)) { rad_free(¤t); RDEBUG2("(%" PRIu64 ") Dropped by attribute filter", count); return; } /* * save the request for later matching */ search.packet = rad_alloc_reply(conf, current); if (!search.packet) { ERROR("Failed allocating memory to hold expected reply"); rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet); rad_free(¤t); return; } search.packet->code = current->code; rs_tv_add_ms(&header->ts, conf->stats.timeout, &when); original = rbtree_finddata(request_tree, &search); /* * Upstream device re-used src/dst ip/port id without waiting * for the timeout period to expire, or a response. */ if (original && memcmp(original->packet->vector, current->vector, sizeof(original->packet->vector) != 0)) { RDEBUG2("(%" PRIu64 ") ** PREMATURE ID RE-USE **", count); stats->exchange[current->code].interval.reused_total++; original->forced_cleanup = true; fr_event_delete(event->list, &original->event); rbtree_deletebydata(request_tree, original); original = NULL; } if (original) { RDEBUG("(%" PRIu64 ") ** RETRANSMISSION **", count); original->rt_req++; rad_free(&original->packet); original->packet = talloc_steal(original, search.packet); /* We may of seen the response, but it may of been lost upstream */ rad_free(&original->linked); fr_event_delete(event->list, &original->event); } else { original = talloc_zero(conf, rs_request_t); talloc_set_destructor(original, _request_free); original->id = count; original->in = event->in; original->stats_req = &stats->exchange[current->code]; original->packet = talloc_steal(original, search.packet); rbtree_insert(request_tree, original); } /* update the timestamp in either case */ original->packet->timestamp = header->ts; if (!fr_event_insert(event->list, rs_packet_cleanup, original, &when, &original->event)) { ERROR("Failed inserting new event"); rbtree_deletebydata(request_tree, original); return; } response = false; } break; default: RDEBUG("** Unsupported code %i **", current->code); rad_free(¤t); return; } if (event->out) { pcap_dump((void *) (event->out->dumper), header, data); } rs_tv_sub(&header->ts, &start_pcap, &elapsed); /* * Increase received count */ stats->exchange[current->code].interval.received_total++; /* * It's a linked response */ if (original && original->linked) { rs_tv_sub(¤t->timestamp, &original->packet->timestamp, &latency); /* * Update stats for both the request and response types. * * This isn't useful for things like Access-Requests, but will be useful for * CoA and Disconnect Messages, as we get the average latency across both * response types. * * It also justifies allocating 255 instances rs_latency_t. */ rs_stats_update_latency(&stats->exchange[current->code], &latency); rs_stats_update_latency(&stats->exchange[original->packet->code], &latency); /* * Print info about the request/response. */ RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000), (unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000)); /* * It's the original request */ } else { /* * Print info about the request */ RIDEBUG("(%" PRIu64 ") %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count, fr_packet_codes[current->code], current->id, event->in->name, fr_inet_ntop(current->src_ipaddr.af, ¤t->src_ipaddr.ipaddr), current->src_port, response ? "<-" : "->", fr_inet_ntop(current->dst_ipaddr.af, ¤t->dst_ipaddr.ipaddr), current->dst_port, (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000)); } if (conf->print_packet && (fr_debug_flag > 1) && current->vps) { pairsort(¤t->vps, true); vp_printlist(log_dst, current->vps); pairfree(¤t->vps); } if (!conf->to_stdout && (fr_debug_flag > 4)) { rad_print_hex(current); } fflush(log_dst); /* * If it's a request, a duplicate of the packet will of already been stored. * If it's a unlinked response, we need to free it explicitly, as it will * not be done by the event queue. */ if (!response || !original) { rad_free(¤t); } captured++; /* * We've hit our capture limit, break out of the event loop */ if ((conf->limit > 0) && (captured >= conf->limit)) { INFO("Captured %" PRIu64 " packets, exiting...", captured); fr_event_loop_exit(events, 1); } }