newvector should be a bool
[freeradius.git] / src / main / radsniff.c
index caae277..1b45593 100644 (file)
@@ -61,9 +61,9 @@ static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
 ", built on " __DATE__ " at " __TIME__;
 
 static int rs_useful_codes[] = {
-       PW_CODE_AUTHENTICATION_REQUEST,         //!< RFC2865 - Authentication request
-       PW_CODE_AUTHENTICATION_ACK,             //!< RFC2865 - Access-Accept
-       PW_CODE_AUTHENTICATION_REJECT,          //!< RFC2865 - Access-Reject
+       PW_CODE_ACCESS_REQUEST,         //!< RFC2865 - Authentication request
+       PW_CODE_ACCESS_ACCEPT,          //!< RFC2865 - Access-Accept
+       PW_CODE_ACCESS_REJECT,          //!< RFC2865 - Access-Reject
        PW_CODE_ACCOUNTING_REQUEST,             //!< RFC2866 - Accounting-Request
        PW_CODE_ACCOUNTING_RESPONSE,            //!< RFC2866 - Accounting-Response
        PW_CODE_ACCESS_CHALLENGE,               //!< RFC2865 - Access-Challenge
@@ -485,9 +485,16 @@ static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t
                        rad_print_hex(packet);
                }
 
-               if (conf->print_packet && (fr_debug_flag > 1) && packet->vps) {
-                       pairsort(&packet->vps, attrtagcmp);
-                       vp_printlist(fr_log_fp, packet->vps);
+               if (conf->print_packet && (fr_debug_flag > 1)) {
+                       char vector[(AUTH_VECTOR_LEN * 2) + 1];
+
+                       if (packet->vps) {
+                               pairsort(&packet->vps, attrtagcmp);
+                               vp_printlist(fr_log_fp, packet->vps);
+                       }
+
+                       fr_bin2hex(vector, packet->vector, AUTH_VECTOR_LEN);
+                       INFO("\tAuthenticator-Field = 0x%s", vector);
                }
        }
 }
@@ -890,6 +897,89 @@ static int rs_packet_cmp(rs_request_t const *a, rs_request_t const *b)
        return fr_packet_cmp(a->expect, b->expect);
 }
 
+static inline int rs_response_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
+                                     uint8_t const *data)
+{
+       if (!event->out) return 0;
+
+       /*
+        *      If we're filtering by response then the requests then the capture buffer
+        *      associated with the request should contain buffered request packets.
+        */
+       if (conf->filter_response) {
+               rs_capture_t *start;
+
+               /*
+                *      Record the current position in the header
+                */
+               start = request->capture_p;
+
+               /*
+                *      Buffer hasn't looped set capture_p to the start of the buffer
+                */
+               if (!start->header) request->capture_p = request->capture;
+
+               /*
+                *      If where capture_p points to, has a header set, write out the
+                *      packet to the PCAP file, looping over the buffer until we
+                *      hit our start point.
+                */
+               if (request->capture_p->header) do {
+                       pcap_dump((void *)event->out->dumper, request->capture_p->header,
+                                 request->capture_p->data);
+                       TALLOC_FREE(request->capture_p->header);
+                       TALLOC_FREE(request->capture_p->data);
+
+                       /* Reset the pointer to the start of the circular buffer */
+                       if (request->capture_p++ >= (request->capture + sizeof(request->capture))) {
+                               request->capture_p = request->capture;
+                       }
+               } while (request->capture_p != start);
+       }
+
+       /*
+        *      Now log the response
+        */
+       pcap_dump((void *)event->out->dumper, header, data);
+
+       return 0;
+}
+
+static inline int rs_request_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
+                                    uint8_t const *data)
+{
+       if (!event->out) return 0;
+
+       /*
+        *      If we're filtering by response, then we need to wait to write out the requests
+        */
+       if (conf->filter_response) {
+               /* Free the old capture */
+               if (request->capture_p->header) {
+                       talloc_free(request->capture_p->header);
+                       TALLOC_FREE(request->capture_p->data);
+               }
+
+               if (!(request->capture_p->header = talloc(request, struct pcap_pkthdr))) return -1;
+               if (!(request->capture_p->data = talloc_array(request, uint8_t, header->caplen))) {
+                       TALLOC_FREE(request->capture_p->header);
+                       return -1;
+               }
+               memcpy(request->capture_p->header, header, sizeof(struct pcap_pkthdr));
+               memcpy(request->capture_p->data, data, header->caplen);
+
+               /* Reset the pointer to the start of the circular buffer */
+               if (++request->capture_p >= (request->capture + sizeof(request->capture))) {
+                       request->capture_p = request->capture;
+               }
+               return 0;
+       }
+
+       pcap_dump((void *)event->out->dumper, header, data);
+
+       return 0;
+}
+
 /* This is the same as immediately scheduling the cleanup event */
 #define RS_CLEANUP_NOW(_x, _s)\
        {\
@@ -995,7 +1085,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                        return;
                }
        }
-       if (version == 4) {
+       if ((version == 4) && conf->verify_udp_checksum) {
                uint16_t expected;
 
                expected = fr_udp_checksum((uint8_t const *) udp, ntohs(udp->len), udp->checksum,
@@ -1013,7 +1103,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
         *      recover once some requests timeout, so make an effort to deal
         *      with allocation failures gracefully.
         */
-       current = rad_alloc(conf, 0);
+       current = rad_alloc(conf, false);
        if (!current) {
                REDEBUG("Failed allocating memory to hold decoded packet");
                rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
@@ -1058,8 +1148,9 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
 
        switch (current->code) {
        case PW_CODE_ACCOUNTING_RESPONSE:
-       case PW_CODE_AUTHENTICATION_REJECT:
-       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCESS_REJECT:
+       case PW_CODE_ACCESS_ACCEPT:
+       case PW_CODE_ACCESS_CHALLENGE:
        case PW_CODE_COA_NAK:
        case PW_CODE_COA_ACK:
        case PW_CODE_DISCONNECT_NAK:
@@ -1112,7 +1203,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                         */
                        if (conf->filter_response_vps) {
                                pairsort(&current->vps, attrtagcmp);
-                               if (!pairvalidate_relaxed(conf->filter_response_vps, current->vps)) {
+                               if (!pairvalidate_relaxed(NULL, conf->filter_response_vps, current->vps)) {
                                        goto drop_response;
                                }
                        }
@@ -1169,12 +1260,13 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                        stats->exchange[current->code].interval.unlinked_total++;
                }
 
+               rs_response_to_pcap(event, original, header, data);
                response = true;
                break;
        }
 
        case PW_CODE_ACCOUNTING_REQUEST:
-       case PW_CODE_AUTHENTICATION_REQUEST:
+       case PW_CODE_ACCESS_REQUEST:
        case PW_CODE_COA_REQUEST:
        case PW_CODE_DISCONNECT_REQUEST:
        case PW_CODE_STATUS_SERVER:
@@ -1257,8 +1349,8 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                 */
                } else {
                        original = rbtree_finddata(request_tree, &search);
-                       if (original && memcmp(original->expect->vector, current->vector,
-                           sizeof(original->expect->vector)) != 0) {
+                       if (original && (memcmp(original->expect->vector, current->vector,
+                           sizeof(original->expect->vector)) != 0)) {
                                /*
                                 *      ID reused before the request timed out (which may be an issue)...
                                 */
@@ -1281,7 +1373,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                 *      Now verify the packet passes the attribute filter
                 */
                if (conf->filter_request_vps) {
-                       if (!pairvalidate_relaxed(conf->filter_request_vps, current->vps)) {
+                       if (!pairvalidate_relaxed(NULL, conf->filter_request_vps, current->vps)) {
                                goto drop_request;
                        }
                }
@@ -1321,6 +1413,9 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                        original->in = event->in;
                        original->stats_req = &stats->exchange[current->code];
 
+                       /* Set the packet pointer to the start of the buffer*/
+                       original->capture_p = original->capture;
+
                        original->packet = talloc_steal(original, current);
                        original->expect = talloc_steal(original, search.expect);
 
@@ -1361,6 +1456,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                        talloc_free(original);
                        return;
                }
+               rs_request_to_pcap(event, original, header, data);
                response = false;
                break;
        }
@@ -1372,10 +1468,6 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                return;
        }
 
-       if (event->out) {
-               pcap_dump((void *) (event->out->dumper), header, data);
-       }
-
        rs_tv_sub(&header->ts, &start_pcap, &elapsed);
 
        /*
@@ -1472,7 +1564,8 @@ static void rs_got_packet(UNUSED fr_event_list_t *el, int fd, void *ctx)
                                DEBUG("Done reading packets (%s)", event->in->name);
                                fr_event_fd_delete(events, 0, fd);
 
-                               if (fr_event_list_num_fds(events) == 0) {
+                               /* Signal pipe takes one slot which is why this is == 1 */
+                               if (fr_event_list_num_fds(events) == 1) {
                                        fr_event_loop_exit(events, 1);
                                }
 
@@ -1576,6 +1669,11 @@ static int rs_build_dict_list(DICT_ATTR const **out, size_t len, char *list)
                i++;
        }
 
+       /*
+        *      This allows efficient list comparisons later
+        */
+       if (i > 1) fr_quick_sort((void const **)out, 0, i - 1, fr_pointer_cmp);
+
        return i;
 }
 
@@ -1605,6 +1703,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
                if (vp->type == VT_XLAT) {
                        vp->type = VT_DATA;
                        vp->vp_strvalue = vp->value.xlat;
+                       vp->length = talloc_array_length(vp->vp_strvalue) - 1;
                }
        }
 
@@ -1616,7 +1715,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
        return 0;
 }
 
-static int rs_build_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
+static int rs_build_event_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
 {
        size_t i = 0;
        char *p, *tok;
@@ -1661,18 +1760,6 @@ static void _unmark_link(void *request)
        this->in_link_tree = false;
 }
 
-/** Write the last signal to the signal pipe
- *
- * @param sig raised
- */
-static void rs_signal_self(int sig)
-{
-       if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
-               ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
-               exit(EXIT_FAILURE);
-       }
-}
-
 #ifdef HAVE_COLLECTDC_H
 /** Re-open the collectd socket
  *
@@ -1699,6 +1786,18 @@ static void rs_collectd_reopen(void *ctx)
 }
 #endif
 
+/** Write the last signal to the signal pipe
+ *
+ * @param sig raised
+ */
+static void rs_signal_self(int sig)
+{
+       if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
+               ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
+}
+
 /** Read the last signal from the signal pipe
  *
  */
@@ -1739,7 +1838,6 @@ static void rs_signal_action(UNUSED fr_event_list_t *list, int fd, UNUSED void *
        }
 }
 
-
 static void NEVER_RETURNS usage(int status)
 {
        FILE *output = status ? stderr : stdout;
@@ -1747,9 +1845,10 @@ static void NEVER_RETURNS usage(int status)
        fprintf(output, "options:\n");
        fprintf(output, "  -a                    List all interfaces available for capture.\n");
        fprintf(output, "  -c <count>            Number of packets to capture.\n");
+       fprintf(output, "  -C                    Enable UDP checksum validation.\n");
        fprintf(output, "  -d <directory>        Set dictionary directory.\n");
-       fprintf(stderr, "  -d <raddb>            Set configuration directory (defaults to " RADDBDIR ").\n");
-       fprintf(stderr, "  -D <dictdir>          Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(output, "  -d <raddb>            Set configuration directory (defaults to " RADDBDIR ").\n");
+       fprintf(output, "  -D <dictdir>          Set main dictionary directory (defaults to " DICTDIR ").\n");
        fprintf(output, "  -e <event>[,<event>]  Only log requests with these event flags.\n");
        fprintf(output, "                        Event may be one of the following:\n");
        fprintf(output, "                        - received - a request or response.\n");
@@ -1774,7 +1873,7 @@ static void NEVER_RETURNS usage(int status)
        fprintf(output, "  -S                    Write PCAP data to stdout.\n");
        fprintf(output, "  -v                    Show program version information.\n");
        fprintf(output, "  -w <file>             Write output packets to file.\n");
-       fprintf(output, "  -x                    Print more debugging information (defaults to -xx).\n");
+       fprintf(output, "  -x                    Print more debugging information.\n");
        fprintf(output, "stats options:\n");
        fprintf(output, "  -W <interval>         Periodically write out statistics every <interval> seconds.\n");
        fprintf(output, "  -T <timeout>          How many milliseconds before the request is counted as lost "
@@ -1854,28 +1953,28 @@ int main(int argc, char *argv[])
        /*
         *  Get options
         */
-       while ((opt = getopt(argc, argv, "ab:c:d:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
+       while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
                switch (opt) {
                case 'a':
-                       {
-                               pcap_if_t *all_devices = NULL;
-                               pcap_if_t *dev_p;
+               {
+                       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;
+                       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;
+               }
+
                /* super secret option */
                case 'b':
                        conf->buffer_pkts = atoi(optarg);
@@ -1893,6 +1992,11 @@ int main(int argc, char *argv[])
                        }
                        break;
 
+               /* udp checksum */
+               case 'C':
+                       conf->verify_udp_checksum = true;
+                       break;
+
                case 'd':
                        radius_dir = optarg;
                        break;
@@ -1902,7 +2006,7 @@ int main(int argc, char *argv[])
                        break;
 
                case 'e':
-                       if (rs_build_flags((int *) &conf->event_flags, rs_events, optarg) < 0) {
+                       if (rs_build_event_flags((int *) &conf->event_flags, rs_events, optarg) < 0) {
                                usage(64);
                        }
                        break;
@@ -1988,6 +2092,10 @@ int main(int argc, char *argv[])
 
                case 'w':
                        out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
+                       if (!out) {
+                               ERROR("Failed creating pcap file \"%s\"", optarg);
+                               exit(EXIT_FAILURE);
+                       }
                        conf->to_file = true;
                        break;
 
@@ -2152,7 +2260,7 @@ int main(int argc, char *argv[])
                        usage(64);
                }
 
-               link_tree = rbtree_create((rbcmp) rs_rtx_cmp, _unmark_link, 0);
+               link_tree = rbtree_create(conf, (rbcmp) rs_rtx_cmp, _unmark_link, 0);
                if (!link_tree) {
                        ERROR("Failed creating RTX tree");
                        goto finish;
@@ -2216,7 +2324,7 @@ int main(int argc, char *argv[])
        /*
         *      Setup the request tree
         */
-       request_tree = rbtree_create((rbcmp) rs_packet_cmp, _unmark_request, 0);
+       request_tree = rbtree_create(conf, (rbcmp) rs_packet_cmp, _unmark_request, 0);
        if (!request_tree) {
                ERROR("Failed creating request tree");
                goto finish;
@@ -2263,7 +2371,7 @@ int main(int argc, char *argv[])
                        DEBUG2("  Device(s)               : [%s]", buff);
                        talloc_free(buff);
                }
-               if (conf->to_file || conf->to_stdout) {
+               if (out) {
                        DEBUG2("  Writing to              : [%s]", out->name);
                }
                if (conf->limit > 0)    {
@@ -2382,7 +2490,7 @@ int main(int argc, char *argv[])
                        }
                }
 
-               assert(out->link_type > 0);
+               assert(out->link_type >= 0);
 
                if (fr_pcap_open(out) < 0) {
                        ERROR("Failed opening pcap output (%s): %s", out->name, fr_strerror());