Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_radius/eap_radius_accounting.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2015-2018 Tobias Brunner
                      3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * Copyright (C) 2012 Martin Willi
                      6:  * Copyright (C) 2012 revosec AG
                      7:  *
                      8:  * This program is free software; you can redistribute it and/or modify it
                      9:  * under the terms of the GNU General Public License as published by the
                     10:  * Free Software Foundation; either version 2 of the License, or (at your
                     11:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful, but
                     14:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     15:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     16:  * for more details.
                     17:  */
                     18: 
                     19: #include "eap_radius_accounting.h"
                     20: #include "eap_radius_provider.h"
                     21: #include "eap_radius_plugin.h"
                     22: 
                     23: #include <time.h>
                     24: 
                     25: #include <radius_message.h>
                     26: #include <radius_client.h>
                     27: #include <daemon.h>
                     28: #include <collections/array.h>
                     29: #include <collections/hashtable.h>
                     30: #include <threading/mutex.h>
                     31: #include <processing/jobs/callback_job.h>
                     32: 
                     33: typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
                     34: 
                     35: /**
                     36:  * Private data of an eap_radius_accounting_t object.
                     37:  */
                     38: struct private_eap_radius_accounting_t {
                     39: 
                     40:        /**
                     41:         * Public eap_radius_accounting_t interface.
                     42:         */
                     43:        eap_radius_accounting_t public;
                     44: 
                     45:        /**
                     46:         * Hashtable with sessions, ike_sa_id_t => entry_t
                     47:         */
                     48:        hashtable_t *sessions;
                     49: 
                     50:        /**
                     51:         * Mutex to lock sessions
                     52:         */
                     53:        mutex_t *mutex;
                     54: 
                     55:        /**
                     56:         * Session ID prefix
                     57:         */
                     58:        uint32_t prefix;
                     59: 
                     60:        /**
                     61:         * Format string we use for Called/Calling-Station-Id for a host
                     62:         */
                     63:        char *station_id_fmt;
                     64: 
                     65:        /**
                     66:         * Disable accounting unless IKE_SA has at least one virtual IP
                     67:         */
                     68:        bool acct_req_vip;
                     69: };
                     70: 
                     71: /**
                     72:  * Singleton instance of accounting
                     73:  */
                     74: static private_eap_radius_accounting_t *singleton = NULL;
                     75: 
                     76: /**
                     77:  * Acct-Terminate-Cause
                     78:  */
                     79: typedef enum {
                     80:        ACCT_CAUSE_USER_REQUEST = 1,
                     81:        ACCT_CAUSE_LOST_CARRIER = 2,
                     82:        ACCT_CAUSE_LOST_SERVICE = 3,
                     83:        ACCT_CAUSE_IDLE_TIMEOUT = 4,
                     84:        ACCT_CAUSE_SESSION_TIMEOUT = 5,
                     85:        ACCT_CAUSE_ADMIN_RESET = 6,
                     86:        ACCT_CAUSE_ADMIN_REBOOT = 7,
                     87:        ACCT_CAUSE_PORT_ERROR = 8,
                     88:        ACCT_CAUSE_NAS_ERROR = 9,
                     89:        ACCT_CAUSE_NAS_REQUEST = 10,
                     90:        ACCT_CAUSE_NAS_REBOOT = 11,
                     91:        ACCT_CAUSE_PORT_UNNEEDED = 12,
                     92:        ACCT_CAUSE_PORT_PREEMPTED = 13,
                     93:        ACCT_CAUSE_PORT_SUSPENDED = 14,
                     94:        ACCT_CAUSE_SERVICE_UNAVAILABLE = 15,
                     95:        ACCT_CAUSE_CALLBACK = 16,
                     96:        ACCT_CAUSE_USER_ERROR = 17,
                     97:        ACCT_CAUSE_HOST_REQUEST = 18,
                     98: } radius_acct_terminate_cause_t;
                     99: 
                    100: /**
                    101:  * Usage stats for bytes and packets
                    102:  */
                    103: typedef struct {
                    104:        struct {
                    105:                uint64_t sent;
                    106:                uint64_t received;
                    107:        } bytes, packets;
                    108: } usage_t;
                    109: 
                    110: /**
                    111:  * Add usage stats (modifies a)
                    112:  */
                    113: static inline void add_usage(usage_t *a, usage_t b)
                    114: {
                    115:        a->bytes.sent += b.bytes.sent;
                    116:        a->bytes.received += b.bytes.received;
                    117:        a->packets.sent += b.packets.sent;
                    118:        a->packets.received += b.packets.received;
                    119: }
                    120: 
                    121: /**
                    122:  * Subtract usage stats (modifies a)
                    123:  */
                    124: static inline void sub_usage(usage_t *a, usage_t b)
                    125: {
                    126:        a->bytes.sent -= b.bytes.sent;
                    127:        a->bytes.received -= b.bytes.received;
                    128:        a->packets.sent -= b.packets.sent;
                    129:        a->packets.received -= b.packets.received;
                    130: }
                    131: 
                    132: /**
                    133:  * Usage stats for a cached/migrated SAs
                    134:  */
                    135: typedef struct {
                    136:        /** unique CHILD_SA identifier */
                    137:        uint32_t id;
                    138:        /** usage stats for this SA */
                    139:        usage_t usage;
                    140: } sa_entry_t;
                    141: 
                    142: /**
                    143:  * Clone an sa_entry_t
                    144:  */
                    145: static sa_entry_t *clone_sa(sa_entry_t *sa)
                    146: {
                    147:        sa_entry_t *this;
                    148: 
                    149:        INIT(this,
                    150:                .id = sa->id,
                    151:                .usage = sa->usage,
                    152:        );
                    153:        return this;
                    154: }
                    155: 
                    156: /**
                    157:  * Hashtable entry with usage stats
                    158:  */
                    159: typedef struct {
                    160:        /** IKE_SA identifier this entry is stored under */
                    161:        ike_sa_id_t *id;
                    162:        /** RADIUS accounting session ID */
                    163:        char sid[24];
                    164:        /** cached Class attributes */
                    165:        array_t *class_attrs;
                    166:        /** number of sent/received octets/packets for expired SAs */
                    167:        usage_t usage;
                    168:        /** list of cached SAs, sa_entry_t (sorted by their unique ID) */
                    169:        array_t *cached;
                    170:        /** list of migrated SAs, sa_entry_t (sorted by their unique ID) */
                    171:        array_t *migrated;
                    172:        /** session creation time */
                    173:        time_t created;
                    174:        /** terminate cause */
                    175:        radius_acct_terminate_cause_t cause;
                    176:        /* interim interval and timestamp of last update */
                    177:        struct {
                    178:                uint32_t interval;
                    179:                time_t last;
                    180:        } interim;
                    181:        /** did we send Accounting-Start */
                    182:        bool start_sent;
                    183: } entry_t;
                    184: 
                    185: /**
                    186:  * Destroy an entry_t
                    187:  */
                    188: static void destroy_entry(entry_t *this)
                    189: {
                    190:        array_destroy_function(this->cached, (void*)free, NULL);
                    191:        array_destroy_function(this->migrated, (void*)free, NULL);
                    192:        array_destroy_function(this->class_attrs, (void*)chunk_free, NULL);
                    193:        this->id->destroy(this->id);
                    194:        free(this);
                    195: }
                    196: 
                    197: /**
                    198:  * Accounting message status types
                    199:  */
                    200: typedef enum {
                    201:        ACCT_STATUS_START = 1,
                    202:        ACCT_STATUS_STOP = 2,
                    203:        ACCT_STATUS_INTERIM_UPDATE = 3,
                    204:        ACCT_STATUS_ACCOUNTING_ON = 7,
                    205:        ACCT_STATUS_ACCOUNTING_OFF = 8,
                    206: } radius_acct_status_t;
                    207: 
                    208: /**
                    209:  * Hashtable hash function
                    210:  */
                    211: static u_int hash(ike_sa_id_t *key)
                    212: {
                    213:        return key->get_responder_spi(key);
                    214: }
                    215: 
                    216: /**
                    217:  * Hashtable equals function
                    218:  */
                    219: static bool equals(ike_sa_id_t *a, ike_sa_id_t *b)
                    220: {
                    221:        return a->equals(a, b);
                    222: }
                    223: 
                    224: /**
                    225:  * Sort cached SAs
                    226:  */
                    227: static int sa_sort(const void *a, const void *b, void *user)
                    228: {
                    229:        const sa_entry_t *ra = a, *rb = b;
                    230:        return ra->id - rb->id;
                    231: }
                    232: 
                    233: /**
                    234:  * Find a cached SA
                    235:  */
                    236: static int sa_find(const void *a, const void *b)
                    237: {
                    238:        return sa_sort(a, b, NULL);
                    239: }
                    240: 
                    241: /**
                    242:  * Update or create usage counters of a cached SA
                    243:  */
                    244: static void update_sa(entry_t *entry, uint32_t id, usage_t usage)
                    245: {
                    246:        sa_entry_t *sa, lookup;
                    247: 
                    248:        lookup.id = id;
                    249:        if (array_bsearch(entry->cached, &lookup, sa_find, &sa) == -1)
                    250:        {
                    251:                INIT(sa,
                    252:                        .id = id,
                    253:                );
                    254:                array_insert_create(&entry->cached, ARRAY_TAIL, sa);
                    255:                array_sort(entry->cached, sa_sort, NULL);
                    256:        }
                    257:        sa->usage = usage;
                    258: }
                    259: 
                    260: /**
                    261:  * Update usage counter when a CHILD_SA rekeys/goes down
                    262:  */
                    263: static void update_usage(private_eap_radius_accounting_t *this,
                    264:                                                 ike_sa_t *ike_sa, child_sa_t *child_sa)
                    265: {
                    266:        usage_t usage;
                    267:        entry_t *entry;
                    268: 
                    269:        child_sa->get_usestats(child_sa, TRUE, NULL, &usage.bytes.received,
                    270:                                                   &usage.packets.received);
                    271:        child_sa->get_usestats(child_sa, FALSE, NULL, &usage.bytes.sent,
                    272:                                                   &usage.packets.sent);
                    273: 
                    274:        this->mutex->lock(this->mutex);
                    275:        entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
                    276:        if (entry)
                    277:        {
                    278:                update_sa(entry, child_sa->get_unique_id(child_sa), usage);
                    279:        }
                    280:        this->mutex->unlock(this->mutex);
                    281: }
                    282: 
                    283: /**
                    284:  * Collect usage stats for all CHILD_SAs of the given IKE_SA, optionally returns
                    285:  * the total number of bytes and packets
                    286:  */
                    287: static array_t *collect_stats(ike_sa_t *ike_sa, usage_t *total)
                    288: {
                    289:        enumerator_t *enumerator;
                    290:        child_sa_t *child_sa;
                    291:        array_t *stats;
                    292:        sa_entry_t *sa;
                    293:        usage_t usage;
                    294: 
                    295:        if (total)
                    296:        {
                    297:                *total = (usage_t){};
                    298:        }
                    299: 
                    300:        stats = array_create(0, 0);
                    301:        enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
                    302:        while (enumerator->enumerate(enumerator, &child_sa))
                    303:        {
                    304:                INIT(sa,
                    305:                        .id = child_sa->get_unique_id(child_sa),
                    306:                );
                    307:                array_insert(stats, ARRAY_TAIL, sa);
                    308:                array_sort(stats, sa_sort, NULL);
                    309: 
                    310:                child_sa->get_usestats(child_sa, TRUE, NULL, &usage.bytes.received,
                    311:                                                           &usage.packets.received);
                    312:                child_sa->get_usestats(child_sa, FALSE, NULL, &usage.bytes.sent,
                    313:                                                           &usage.packets.sent);
                    314:                sa->usage = usage;
                    315:                if (total)
                    316:                {
                    317:                        add_usage(total, usage);
                    318:                }
                    319:        }
                    320:        enumerator->destroy(enumerator);
                    321:        return stats;
                    322: }
                    323: 
                    324: /**
                    325:  * Cleanup cached SAs
                    326:  */
                    327: static void cleanup_sas(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
                    328:                                                entry_t *entry)
                    329: {
                    330:        enumerator_t *enumerator;
                    331:        child_sa_t *child_sa;
                    332:        sa_entry_t *sa, *found;
                    333:        array_t *sas;
                    334: 
                    335:        sas = array_create(0, 0);
                    336:        enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
                    337:        while (enumerator->enumerate(enumerator, &child_sa))
                    338:        {
                    339:                INIT(sa,
                    340:                        .id = child_sa->get_unique_id(child_sa),
                    341:                );
                    342:                array_insert(sas, ARRAY_TAIL, sa);
                    343:                array_sort(sas, sa_sort, NULL);
                    344:        }
                    345:        enumerator->destroy(enumerator);
                    346: 
                    347:        enumerator = array_create_enumerator(entry->cached);
                    348:        while (enumerator->enumerate(enumerator, &sa))
                    349:        {
                    350:                if (array_bsearch(sas, sa, sa_find, &found) == -1)
                    351:                {
                    352:                        /* SA is gone, add its latest stats to the total for this IKE_SA
                    353:                         * and remove the cache entry */
                    354:                        add_usage(&entry->usage, sa->usage);
                    355:                        array_remove_at(entry->cached, enumerator);
                    356:                        free(sa);
                    357:                }
                    358:        }
                    359:        enumerator->destroy(enumerator);
                    360:        enumerator = array_create_enumerator(entry->migrated);
                    361:        while (enumerator->enumerate(enumerator, &sa))
                    362:        {
                    363:                if (array_bsearch(sas, sa, sa_find, &found) == -1)
                    364:                {
                    365:                        /* SA is gone, subtract stats from the total for this IKE_SA */
                    366:                        sub_usage(&entry->usage, sa->usage);
                    367:                        array_remove_at(entry->migrated, enumerator);
                    368:                        free(sa);
                    369:                }
                    370:        }
                    371:        enumerator->destroy(enumerator);
                    372:        array_destroy_function(sas, (void*)free, NULL);
                    373: }
                    374: 
                    375: /**
                    376:  * Send a RADIUS message, wait for response
                    377:  */
                    378: static bool send_message(private_eap_radius_accounting_t *this,
                    379:                                                 radius_message_t *request)
                    380: {
                    381:        radius_message_t *response;
                    382:        radius_client_t *client;
                    383:        bool ack = FALSE;
                    384: 
                    385:        client = eap_radius_create_client();
                    386:        if (client)
                    387:        {
                    388:                response = client->request(client, request);
                    389:                if (response)
                    390:                {
                    391:                        ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
                    392:                        response->destroy(response);
                    393:                }
                    394:                client->destroy(client);
                    395:        }
                    396:        return ack;
                    397: }
                    398: 
                    399: /**
                    400:  * Add common IKE_SA parameters to RADIUS account message
                    401:  */
                    402: static void add_ike_sa_parameters(private_eap_radius_accounting_t *this,
                    403:                                                                  radius_message_t *message, ike_sa_t *ike_sa)
                    404: {
                    405:        enumerator_t *enumerator;
                    406:        host_t *vip, *host;
                    407:        char buf[MAX_RADIUS_ATTRIBUTE_SIZE + 1];
                    408:        chunk_t data;
                    409:        uint32_t value;
                    410: 
                    411:        /* virtual NAS-Port-Type */
                    412:        value = htonl(5);
                    413:        message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
                    414:        /* framed ServiceType */
                    415:        value = htonl(2);
                    416:        message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value));
                    417: 
                    418:        value = htonl(ike_sa->get_unique_id(ike_sa));
                    419:        message->add(message, RAT_NAS_PORT, chunk_from_thing(value));
                    420:        message->add(message, RAT_NAS_PORT_ID,
                    421:                                 chunk_from_str(ike_sa->get_name(ike_sa)));
                    422: 
                    423:        host = ike_sa->get_my_host(ike_sa);
                    424:        data = host->get_address(host);
                    425:        switch (host->get_family(host))
                    426:        {
                    427:                case AF_INET:
                    428:                        message->add(message, RAT_NAS_IP_ADDRESS, data);
                    429:                        break;
                    430:                case AF_INET6:
                    431:                        message->add(message, RAT_NAS_IPV6_ADDRESS, data);
                    432:                default:
                    433:                        break;
                    434:        }
                    435:        snprintf(buf, sizeof(buf), this->station_id_fmt, host);
                    436:        message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf));
                    437:        host = ike_sa->get_other_host(ike_sa);
                    438:        snprintf(buf, sizeof(buf), this->station_id_fmt, host);
                    439:        message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf));
                    440: 
                    441:        snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
                    442:        message->add(message, RAT_USER_NAME, chunk_from_str(buf));
                    443: 
                    444:        enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
                    445:        while (enumerator->enumerate(enumerator, &vip))
                    446:        {
                    447:                switch (vip->get_family(vip))
                    448:                {
                    449:                        case AF_INET:
                    450:                                message->add(message, RAT_FRAMED_IP_ADDRESS,
                    451:                                                         vip->get_address(vip));
                    452:                                break;
                    453:                        case AF_INET6:
                    454:                                message->add(message, RAT_FRAMED_IPV6_ADDRESS,
                    455:                                                         vip->get_address(vip));
                    456:                                break;
                    457:                        default:
                    458:                                break;
                    459:                }
                    460:        }
                    461:        enumerator->destroy(enumerator);
                    462: }
                    463: 
                    464: /**
                    465:  * Add any unclaimed IP addresses to the message
                    466:  */
                    467: static void add_unclaimed_ips(radius_message_t *message, ike_sa_t *ike_sa)
                    468: {
                    469:        eap_radius_provider_t *provider;
                    470:        enumerator_t *enumerator;
                    471:        host_t *vip;
                    472: 
                    473:        provider = eap_radius_provider_get();
                    474:        enumerator = provider->clear_unclaimed(provider,
                    475:                                                                                   ike_sa->get_unique_id(ike_sa));
                    476:        while (enumerator->enumerate(enumerator, &vip))
                    477:        {
                    478:                switch (vip->get_family(vip))
                    479:                {
                    480:                        case AF_INET:
                    481:                                message->add(message, RAT_FRAMED_IP_ADDRESS,
                    482:                                                         vip->get_address(vip));
                    483:                                break;
                    484:                        case AF_INET6:
                    485:                                message->add(message, RAT_FRAMED_IPV6_ADDRESS,
                    486:                                                         vip->get_address(vip));
                    487:                                break;
                    488:                        default:
                    489:                                break;
                    490:                }
                    491:        }
                    492:        enumerator->destroy(enumerator);
                    493: }
                    494: 
                    495: /**
                    496:  * Add the Class attributes received in the Access-Accept message to the
                    497:  * RADIUS accounting message
                    498:  */
                    499: static void add_class_attributes(radius_message_t *message, entry_t *entry)
                    500: {
                    501:        enumerator_t *enumerator;
                    502:        chunk_t *cls;
                    503: 
                    504:        enumerator = array_create_enumerator(entry->class_attrs);
                    505:        while (enumerator->enumerate(enumerator, &cls))
                    506:        {
                    507:                message->add(message, RAT_CLASS, *cls);
                    508:        }
                    509:        enumerator->destroy(enumerator);
                    510: }
                    511: 
                    512: /**
                    513:  * Get an existing or create a new entry from the locked session table
                    514:  */
                    515: static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this,
                    516:                                                                        ike_sa_id_t *id, uint32_t unique)
                    517: {
                    518:        entry_t *entry;
                    519:        time_t now;
                    520: 
                    521:        entry = this->sessions->get(this->sessions, id);
                    522:        if (!entry)
                    523:        {
                    524:                now = time_monotonic(NULL);
                    525: 
                    526:                INIT(entry,
                    527:                        .id = id->clone(id),
                    528:                        .created = now,
                    529:                        .interim = {
                    530:                                .last = now,
                    531:                        },
                    532:                        /* default terminate cause, if none other caught */
                    533:                        .cause = ACCT_CAUSE_USER_REQUEST,
                    534:                );
                    535:                snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, unique);
                    536:                this->sessions->put(this->sessions, entry->id, entry);
                    537:        }
                    538:        return entry;
                    539: }
                    540: 
                    541: /* forward declaration */
                    542: static void schedule_interim(private_eap_radius_accounting_t *this,
                    543:                                                         entry_t *entry);
                    544: 
                    545: /**
                    546:  * Data passed to send_interim() using callback job
                    547:  */
                    548: typedef struct {
                    549:        /** reference to radius accounting */
                    550:        private_eap_radius_accounting_t *this;
                    551:        /** IKE_SA identifier to send interim update to */
                    552:        ike_sa_id_t *id;
                    553: } interim_data_t;
                    554: 
                    555: /**
                    556:  * Clean up interim data
                    557:  */
                    558: void destroy_interim_data(interim_data_t *this)
                    559: {
                    560:        this->id->destroy(this->id);
                    561:        free(this);
                    562: }
                    563: 
                    564: /**
                    565:  * Send an interim update for entry of given IKE_SA identifier
                    566:  */
                    567: static job_requeue_t send_interim(interim_data_t *data)
                    568: {
                    569:        private_eap_radius_accounting_t *this = data->this;
                    570:        usage_t usage;
                    571:        radius_message_t *message = NULL;
                    572:        enumerator_t *enumerator;
                    573:        ike_sa_t *ike_sa;
                    574:        entry_t *entry;
                    575:        uint32_t value;
                    576:        array_t *stats;
                    577:        sa_entry_t *sa, *found;
                    578: 
                    579:        ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
                    580:        if (!ike_sa)
                    581:        {
                    582:                return JOB_REQUEUE_NONE;
                    583:        }
                    584:        stats = collect_stats(ike_sa, &usage);
                    585:        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                    586: 
                    587:        /* avoid any races by returning IKE_SA before acquiring lock */
                    588: 
                    589:        this->mutex->lock(this->mutex);
                    590:        entry = this->sessions->get(this->sessions, data->id);
                    591:        if (entry)
                    592:        {
                    593:                entry->interim.last = time_monotonic(NULL);
                    594: 
                    595:                enumerator = array_create_enumerator(entry->cached);
                    596:                while (enumerator->enumerate(enumerator, &sa))
                    597:                {
                    598:                        if (array_bsearch(stats, sa, sa_find, &found) != -1)
                    599:                        {
                    600:                                /* SA is still around, update stats (e.g. for IKEv1 where
                    601:                                 * SA might get used even after rekeying) */
                    602:                                sa->usage = found->usage;
                    603:                        }
                    604:                        else
                    605:                        {
                    606:                                /* SA is gone, add its last stats to the total for this IKE_SA
                    607:                                 * and remove the cache entry */
                    608:                                add_usage(&entry->usage, sa->usage);
                    609:                                array_remove_at(entry->cached, enumerator);
                    610:                                free(sa);
                    611:                        }
                    612:                }
                    613:                enumerator->destroy(enumerator);
                    614: 
                    615:                enumerator = array_create_enumerator(entry->migrated);
                    616:                while (enumerator->enumerate(enumerator, &sa))
                    617:                {
                    618:                        if (array_bsearch(stats, sa, sa_find, &found) != -1)
                    619:                        {
                    620:                                /* SA is still around, but we have to compensate */
                    621:                                sub_usage(&usage, sa->usage);
                    622:                        }
                    623:                        else
                    624:                        {
                    625:                                /* SA is gone, subtract stats from the total for this IKE_SA */
                    626:                                sub_usage(&entry->usage, sa->usage);
                    627:                                array_remove_at(entry->migrated, enumerator);
                    628:                                free(sa);
                    629:                        }
                    630:                }
                    631:                enumerator->destroy(enumerator);
                    632: 
                    633:                add_usage(&usage, entry->usage);
                    634: 
                    635:                message = radius_message_create(RMC_ACCOUNTING_REQUEST);
                    636:                value = htonl(ACCT_STATUS_INTERIM_UPDATE);
                    637:                message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
                    638:                message->add(message, RAT_ACCT_SESSION_ID,
                    639:                                         chunk_create(entry->sid, strlen(entry->sid)));
                    640:                add_class_attributes(message, entry);
                    641:                add_ike_sa_parameters(this, message, ike_sa);
                    642: 
                    643:                value = htonl(usage.bytes.sent);
                    644:                message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
                    645:                value = htonl(usage.bytes.sent >> 32);
                    646:                if (value)
                    647:                {
                    648:                        message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
                    649:                                                 chunk_from_thing(value));
                    650:                }
                    651:                value = htonl(usage.packets.sent);
                    652:                message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
                    653: 
                    654:                value = htonl(usage.bytes.received);
                    655:                message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
                    656:                value = htonl(usage.bytes.received >> 32);
                    657:                if (value)
                    658:                {
                    659:                        message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
                    660:                                                 chunk_from_thing(value));
                    661:                }
                    662:                value = htonl(usage.packets.received);
                    663:                message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
                    664: 
                    665:                value = htonl(entry->interim.last - entry->created);
                    666:                message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
                    667: 
                    668:                schedule_interim(this, entry);
                    669:        }
                    670:        this->mutex->unlock(this->mutex);
                    671:        array_destroy_function(stats, (void*)free, NULL);
                    672: 
                    673:        if (message)
                    674:        {
                    675:                if (!send_message(this, message))
                    676:                {
                    677:                        if (lib->settings->get_bool(lib->settings,
                    678:                                                        "%s.plugins.eap-radius.accounting_close_on_timeout",
                    679:                                                        TRUE, lib->ns))
                    680:                        {
                    681:                                eap_radius_handle_timeout(data->id);
                    682:                        }
                    683:                }
                    684:                message->destroy(message);
                    685:        }
                    686:        return JOB_REQUEUE_NONE;
                    687: }
                    688: 
                    689: /**
                    690:  * Schedule interim update for given entry
                    691:  */
                    692: static void schedule_interim(private_eap_radius_accounting_t *this,
                    693:                                                         entry_t *entry)
                    694: {
                    695:        if (entry->interim.interval)
                    696:        {
                    697:                interim_data_t *data;
                    698:                timeval_t tv = {
                    699:                        .tv_sec = entry->interim.last + entry->interim.interval,
                    700:                };
                    701: 
                    702:                INIT(data,
                    703:                        .this = this,
                    704:                        .id = entry->id->clone(entry->id),
                    705:                );
                    706:                lib->scheduler->schedule_job_tv(lib->scheduler,
                    707:                        (job_t*)callback_job_create_with_prio(
                    708:                                (callback_job_cb_t)send_interim,
                    709:                                data, (void*)destroy_interim_data,
                    710:                                (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv);
                    711:        }
                    712: }
                    713: 
                    714: /**
                    715:  * Check if an IKE_SA has assigned a virtual IP (to peer)
                    716:  */
                    717: static bool has_vip(ike_sa_t *ike_sa)
                    718: {
                    719:        enumerator_t *enumerator;
                    720:        host_t *host;
                    721:        bool found;
                    722: 
                    723:        enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
                    724:        found = enumerator->enumerate(enumerator, &host);
                    725:        enumerator->destroy(enumerator);
                    726: 
                    727:        return found;
                    728: }
                    729: 
                    730: /**
                    731:  * Send an accounting start message
                    732:  */
                    733: static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
                    734: {
                    735:        radius_message_t *message;
                    736:        entry_t *entry;
                    737:        uint32_t value;
                    738: 
                    739:        if (this->acct_req_vip && !has_vip(ike_sa))
                    740:        {
                    741:                return;
                    742:        }
                    743: 
                    744:        this->mutex->lock(this->mutex);
                    745: 
                    746:        entry = get_or_create_entry(this, ike_sa->get_id(ike_sa),
                    747:                                                                ike_sa->get_unique_id(ike_sa));
                    748:        if (entry->start_sent)
                    749:        {
                    750:                this->mutex->unlock(this->mutex);
                    751:                return;
                    752:        }
                    753:        entry->start_sent = TRUE;
                    754: 
                    755:        message = radius_message_create(RMC_ACCOUNTING_REQUEST);
                    756:        value = htonl(ACCT_STATUS_START);
                    757:        message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
                    758:        message->add(message, RAT_ACCT_SESSION_ID,
                    759:                                 chunk_create(entry->sid, strlen(entry->sid)));
                    760:        add_class_attributes(message, entry);
                    761: 
                    762:        if (!entry->interim.interval)
                    763:        {
                    764:                entry->interim.interval = lib->settings->get_time(lib->settings,
                    765:                                        "%s.plugins.eap-radius.accounting_interval", 0, lib->ns);
                    766:                if (entry->interim.interval)
                    767:                {
                    768:                        DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us",
                    769:                                 entry->interim.interval);
                    770:                }
                    771:        }
                    772:        schedule_interim(this, entry);
                    773:        this->mutex->unlock(this->mutex);
                    774: 
                    775:        add_ike_sa_parameters(this, message, ike_sa);
                    776:        if (!send_message(this, message))
                    777:        {
                    778:                eap_radius_handle_timeout(ike_sa->get_id(ike_sa));
                    779:        }
                    780:        message->destroy(message);
                    781: }
                    782: 
                    783: /**
                    784:  * Send an account stop message
                    785:  */
                    786: static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
                    787: {
                    788:        radius_message_t *message;
                    789:        enumerator_t *enumerator;
                    790:        entry_t *entry;
                    791:        sa_entry_t *sa;
                    792:        uint32_t value;
                    793: 
                    794:        this->mutex->lock(this->mutex);
                    795:        entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa));
                    796:        this->mutex->unlock(this->mutex);
                    797:        if (entry)
                    798:        {
                    799:                if (!entry->start_sent)
                    800:                {       /* we tried to authenticate this peer, but never sent a start */
                    801:                        destroy_entry(entry);
                    802:                        return;
                    803:                }
                    804:                enumerator = array_create_enumerator(entry->cached);
                    805:                while (enumerator->enumerate(enumerator, &sa))
                    806:                {
                    807:                        add_usage(&entry->usage, sa->usage);
                    808:                }
                    809:                enumerator->destroy(enumerator);
                    810: 
                    811:                enumerator = array_create_enumerator(entry->migrated);
                    812:                while (enumerator->enumerate(enumerator, &sa))
                    813:                {
                    814:                        sub_usage(&entry->usage, sa->usage);
                    815:                }
                    816:                enumerator->destroy(enumerator);
                    817: 
                    818:                message = radius_message_create(RMC_ACCOUNTING_REQUEST);
                    819:                value = htonl(ACCT_STATUS_STOP);
                    820:                message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
                    821:                message->add(message, RAT_ACCT_SESSION_ID,
                    822:                                         chunk_create(entry->sid, strlen(entry->sid)));
                    823:                add_class_attributes(message, entry);
                    824:                add_ike_sa_parameters(this, message, ike_sa);
                    825:                add_unclaimed_ips(message, ike_sa);
                    826: 
                    827:                value = htonl(entry->usage.bytes.sent);
                    828:                message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
                    829:                value = htonl(entry->usage.bytes.sent >> 32);
                    830:                if (value)
                    831:                {
                    832:                        message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
                    833:                                                 chunk_from_thing(value));
                    834:                }
                    835:                value = htonl(entry->usage.packets.sent);
                    836:                message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
                    837: 
                    838:                value = htonl(entry->usage.bytes.received);
                    839:                message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
                    840:                value = htonl(entry->usage.bytes.received >> 32);
                    841:                if (value)
                    842:                {
                    843:                        message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
                    844:                                                 chunk_from_thing(value));
                    845:                }
                    846:                value = htonl(entry->usage.packets.received);
                    847:                message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
                    848: 
                    849:                value = htonl(time_monotonic(NULL) - entry->created);
                    850:                message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
                    851: 
                    852:                value = htonl(entry->cause);
                    853:                message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value));
                    854: 
                    855:                if (!send_message(this, message))
                    856:                {
                    857:                        eap_radius_handle_timeout(NULL);
                    858:                }
                    859:                message->destroy(message);
                    860:                destroy_entry(entry);
                    861:        }
                    862: }
                    863: 
                    864: METHOD(listener_t, alert, bool,
                    865:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert,
                    866:        va_list args)
                    867: {
                    868:        radius_acct_terminate_cause_t cause;
                    869:        entry_t *entry;
                    870: 
                    871:        switch (alert)
                    872:        {
                    873:                case ALERT_IKE_SA_EXPIRED:
                    874:                        cause = ACCT_CAUSE_SESSION_TIMEOUT;
                    875:                        break;
                    876:                case ALERT_RETRANSMIT_SEND_TIMEOUT:
                    877:                        cause = ACCT_CAUSE_LOST_SERVICE;
                    878:                        break;
                    879:                default:
                    880:                        return TRUE;
                    881:        }
                    882:        this->mutex->lock(this->mutex);
                    883:        entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
                    884:        if (entry)
                    885:        {
                    886:                entry->cause = cause;
                    887:        }
                    888:        this->mutex->unlock(this->mutex);
                    889:        return TRUE;
                    890: }
                    891: 
                    892: METHOD(listener_t, ike_updown, bool,
                    893:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
                    894: {
                    895:        if (!up)
                    896:        {
                    897:                enumerator_t *enumerator;
                    898:                child_sa_t *child_sa;
                    899: 
                    900:                /* update usage for all children just before sending stop */
                    901:                enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
                    902:                while (enumerator->enumerate(enumerator, &child_sa))
                    903:                {
                    904:                        update_usage(this, ike_sa, child_sa);
                    905:                }
                    906:                enumerator->destroy(enumerator);
                    907: 
                    908:                send_stop(this, ike_sa);
                    909:        }
                    910:        return TRUE;
                    911: }
                    912: 
                    913: METHOD(listener_t, message_hook, bool,
                    914:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
                    915:        message_t *message, bool incoming, bool plain)
                    916: {
                    917:        /* start accounting here, virtual IP now is set */
                    918:        if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
                    919:                !incoming && !message->get_request(message))
                    920:        {
                    921:                if (ike_sa->get_version(ike_sa) == IKEV2 &&
                    922:                        message->get_exchange_type(message) == IKE_AUTH)
                    923:                {
                    924:                        send_start(this, ike_sa);
                    925:                }
                    926:        }
                    927:        return TRUE;
                    928: }
                    929: 
                    930: METHOD(listener_t, assign_vips, bool,
                    931:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool assign)
                    932: {
                    933:        /* start accounting as soon as the virtual IP is set */
                    934:        if (assign && ike_sa->get_version(ike_sa) == IKEV1)
                    935:        {
                    936:                send_start(this, ike_sa);
                    937:        }
                    938:        return TRUE;
                    939: }
                    940: 
                    941: METHOD(listener_t, ike_rekey, bool,
                    942:        private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
                    943: {
                    944:        entry_t *entry;
                    945: 
                    946:        this->mutex->lock(this->mutex);
                    947:        entry = this->sessions->remove(this->sessions, old->get_id(old));
                    948:        if (entry)
                    949:        {
                    950:                /* update IKE_SA identifier */
                    951:                entry->id->destroy(entry->id);
                    952:                entry->id = new->get_id(new);
                    953:                entry->id = entry->id->clone(entry->id);
                    954:                /* fire new interim update job, old gets invalid */
                    955:                schedule_interim(this, entry);
                    956: 
                    957:                cleanup_sas(this, new, entry);
                    958: 
                    959:                entry = this->sessions->put(this->sessions, entry->id, entry);
                    960:                if (entry)
                    961:                {
                    962:                        destroy_entry(entry);
                    963:                }
                    964:        }
                    965:        this->mutex->unlock(this->mutex);
                    966: 
                    967:        return TRUE;
                    968: }
                    969: 
                    970: METHOD(listener_t, child_rekey, bool,
                    971:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
                    972:        child_sa_t *old, child_sa_t *new)
                    973: {
                    974:        entry_t *entry;
                    975: 
                    976:        update_usage(this, ike_sa, old);
                    977:        this->mutex->lock(this->mutex);
                    978:        entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
                    979:        if (entry)
                    980:        {
                    981:                cleanup_sas(this, ike_sa, entry);
                    982:        }
                    983:        this->mutex->unlock(this->mutex);
                    984:        return TRUE;
                    985: }
                    986: 
                    987: METHOD(listener_t, children_migrate, bool,
                    988:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, ike_sa_id_t *new,
                    989:        uint32_t unique)
                    990: {
                    991:        enumerator_t *enumerator;
                    992:        sa_entry_t *sa, *sa_new, *cached;
                    993:        entry_t *entry_old, *entry_new;
                    994:        array_t *stats;
                    995: 
                    996:        if (!new)
                    997:        {
                    998:                return TRUE;
                    999:        }
                   1000:        stats = collect_stats(ike_sa, NULL);
                   1001:        this->mutex->lock(this->mutex);
                   1002:        entry_old = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
                   1003:        if (entry_old)
                   1004:        {
                   1005:                entry_new = get_or_create_entry(this, new, unique);
                   1006:                enumerator = array_create_enumerator(stats);
                   1007:                while (enumerator->enumerate(enumerator, &sa))
                   1008:                {
                   1009:                        /* if the SA was already rekeyed/cached we cache it too on the new
                   1010:                         * SA to track it properly until it's finally gone */
                   1011:                        if (array_bsearch(entry_old->cached, sa, sa_find, &cached) != -1)
                   1012:                        {
                   1013:                                sa_new = clone_sa(sa);
                   1014:                                array_insert_create(&entry_new->cached, ARRAY_TAIL, sa_new);
                   1015:                                array_sort(entry_new->cached, sa_sort, NULL);
                   1016:                        }
                   1017:                        /* if the SA was used, we store it to compensate on the new SA */
                   1018:                        if (sa->usage.bytes.sent || sa->usage.bytes.received ||
                   1019:                                sa->usage.packets.sent || sa->usage.packets.received)
                   1020:                        {
                   1021:                                sa_new = clone_sa(sa);
                   1022:                                array_insert_create(&entry_new->migrated, ARRAY_TAIL, sa_new);
                   1023:                                array_sort(entry_new->migrated, sa_sort, NULL);
                   1024:                                /* store/update latest stats on old SA to report in Stop */
                   1025:                                update_sa(entry_old, sa->id, sa->usage);
                   1026:                        }
                   1027:                }
                   1028:                enumerator->destroy(enumerator);
                   1029:        }
                   1030:        this->mutex->unlock(this->mutex);
                   1031:        array_destroy_function(stats, (void*)free, NULL);
                   1032:        return TRUE;
                   1033: }
                   1034: 
                   1035: METHOD(listener_t, child_updown, bool,
                   1036:        private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
                   1037:        child_sa_t *child_sa, bool up)
                   1038: {
                   1039:        if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
                   1040:        {
                   1041:                update_usage(this, ike_sa, child_sa);
                   1042:        }
                   1043:        return TRUE;
                   1044: }
                   1045: 
                   1046: METHOD(eap_radius_accounting_t, destroy, void,
                   1047:        private_eap_radius_accounting_t *this)
                   1048: {
                   1049:        charon->bus->remove_listener(charon->bus, &this->public.listener);
                   1050:        singleton = NULL;
                   1051:        this->mutex->destroy(this->mutex);
                   1052:        this->sessions->destroy(this->sessions);
                   1053:        free(this);
                   1054: }
                   1055: 
                   1056: /**
                   1057:  * See header
                   1058:  */
                   1059: eap_radius_accounting_t *eap_radius_accounting_create()
                   1060: {
                   1061:        private_eap_radius_accounting_t *this;
                   1062: 
                   1063:        INIT(this,
                   1064:                .public = {
                   1065:                        .listener = {
                   1066:                                .alert = _alert,
                   1067:                                .ike_updown = _ike_updown,
                   1068:                                .ike_rekey = _ike_rekey,
                   1069:                                .message = _message_hook,
                   1070:                                .assign_vips = _assign_vips,
                   1071:                                .child_updown = _child_updown,
                   1072:                                .child_rekey = _child_rekey,
                   1073:                                .children_migrate = _children_migrate,
                   1074:                        },
                   1075:                        .destroy = _destroy,
                   1076:                },
                   1077:                /* use system time as Session ID prefix */
                   1078:                .prefix = (uint32_t)time(NULL),
                   1079:                .sessions = hashtable_create((hashtable_hash_t)hash,
                   1080:                                                                         (hashtable_equals_t)equals, 32),
                   1081:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
                   1082:        );
                   1083:        if (lib->settings->get_bool(lib->settings,
                   1084:                                "%s.plugins.eap-radius.station_id_with_port", TRUE, lib->ns))
                   1085:        {
                   1086:                this->station_id_fmt = "%#H";
                   1087:        }
                   1088:        else
                   1089:        {
                   1090:                this->station_id_fmt = "%H";
                   1091:        }
                   1092:        if (lib->settings->get_bool(lib->settings,
                   1093:                                                        "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
                   1094:        {
                   1095:                singleton = this;
                   1096:                charon->bus->add_listener(charon->bus, &this->public.listener);
                   1097:        }
                   1098:        this->acct_req_vip = lib->settings->get_bool(lib->settings,
                   1099:                                                        "%s.plugins.eap-radius.accounting_requires_vip",
                   1100:                                                        FALSE, lib->ns);
                   1101: 
                   1102:        return &this->public;
                   1103: }
                   1104: 
                   1105: /*
                   1106:  * Described in header
                   1107:  */
                   1108: char *eap_radius_accounting_session_id(ike_sa_t *ike_sa)
                   1109: {
                   1110:        entry_t *entry;
                   1111:        char *sid = NULL;
                   1112: 
                   1113:        if (singleton)
                   1114:        {
                   1115:                singleton->mutex->lock(singleton->mutex);
                   1116:                entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa),
                   1117:                                                                        ike_sa->get_unique_id(ike_sa));
                   1118:                sid = strdup(entry->sid);
                   1119:                singleton->mutex->unlock(singleton->mutex);
                   1120:        }
                   1121:        return sid;
                   1122: }
                   1123: 
                   1124: /*
                   1125:  * Described in header
                   1126:  */
                   1127: void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, uint32_t interval)
                   1128: {
                   1129:        if (singleton)
                   1130:        {
                   1131:                entry_t *entry;
                   1132: 
                   1133:                DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval);
                   1134:                singleton->mutex->lock(singleton->mutex);
                   1135:                entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa),
                   1136:                                                                        ike_sa->get_unique_id(ike_sa));
                   1137:                entry->interim.interval = interval;
                   1138:                singleton->mutex->unlock(singleton->mutex);
                   1139:        }
                   1140: }
                   1141: 
                   1142: /*
                   1143:  * Described in header
                   1144:  */
                   1145: void eap_radius_accounting_add_class(ike_sa_t *ike_sa, chunk_t cls)
                   1146: {
                   1147:        if (singleton)
                   1148:        {
                   1149:                entry_t *entry;
                   1150:                chunk_t clone;
                   1151: 
                   1152:                DBG2(DBG_CFG, "cache RADIUS Class attribute %B", &cls);
                   1153:                singleton->mutex->lock(singleton->mutex);
                   1154:                entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa),
                   1155:                                                                        ike_sa->get_unique_id(ike_sa));
                   1156:                clone = chunk_clone(cls);
                   1157:                array_insert_create_value(&entry->class_attrs, sizeof(chunk_t),
                   1158:                                                                  ARRAY_TAIL, &clone);
                   1159:                singleton->mutex->unlock(singleton->mutex);
                   1160:        }
                   1161: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>