totofugaのブログ

ネットワークとかc言語、perlの話。

linux arpの内部データ

arpの統計情報が欲しかったのでnetlink経由で取得するものを作った NDTA_PARMSはrtattrが入れ子になっている。

#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/neighbour.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>

static int seq;

void msg_receive(int sock) {
    char buf[4096]; 

    int n = read(sock, buf, sizeof(buf));
    if ( n <= 0 ) {
        perror("read error\n");
        exit(1);
    }
    struct nlmsghdr *nh = (struct nlmsghdr *)buf;
    // printf("read size: %d\n", n);
    if ( nh->nlmsg_seq != seq ) {
        printf("seq error\n");
        exit(1);
    }
    if ( nh->nlmsg_type == NLMSG_ERROR ) {
        printf("error\n");
        exit(1);
    } else if ( nh->nlmsg_type == RTM_NEWNEIGHTBL ) {
        NLMSG_ALIGN(nh->nlmsg_len);
        struct ndtmsg *ndtm = NLMSG_DATA(nh);
        if ( ndtm->ndtm_family != AF_INET ) {
            printf("ndgm_family is not AF_INET\n");
            exit(1);
        }

        struct rtattr *rta;
        int len = nh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndtmsg));
        for (rta = (struct rtattr*)((char *)ndtm + sizeof(struct ndtmsg)); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
            switch(rta->rta_type) {
                case NDTA_NAME:
                    printf("NAME: %s\n", (char*)RTA_DATA(rta));
                    break;
                case NDTA_THRESH1:
                    printf("THRESH1 %d\n", *(uint32_t*)RTA_DATA(rta));
                    break;
                case NDTA_THRESH2:
                    printf("THRESH2 %d\n", *(uint32_t*)RTA_DATA(rta));
                    break;
                case NDTA_THRESH3:
                    printf("THRESH3 %d\n", *(uint32_t*)RTA_DATA(rta));
                    break;
                case NDTA_CONFIG:
                     {
                        struct ndt_config *config = (struct ndt_config*)RTA_DATA(rta);
                        printf("CONFIG:\n");
                        printf("\tkey_len: %d\n", config->ndtc_key_len);
                        printf("\tentry_size: %d\n", config->ndtc_entry_size);
                        printf("\tentries: %d\n", config->ndtc_entries);
                        printf("\tlast_flush: %d\n", config->ndtc_last_flush);
                        printf("\tlast_rand: %d\n", config->ndtc_last_rand);
                        printf("\thash_rnd: %d\n", config->ndtc_hash_rnd);
                        printf("\thash_mask: %d\n", config->ndtc_hash_mask);
                        printf("\thash_chain_gc: %d\n", config->ndtc_hash_chain_gc);
                        printf("\tproxy_qlen: %d\n", config->ndtc_proxy_qlen);
                    }
                    break;
                case NDTA_PARMS:
                    {
                        printf("PARAMS:\n");
                        struct rtattr *parm_rta;
                        int parm_len = rta->rta_len;
                        for (parm_rta = rta + 1; RTA_OK(parm_rta, parm_len); parm_rta = RTA_NEXT(parm_rta, parm_len)) {
                            switch(parm_rta->rta_type) {
                                case NDTPA_IFINDEX:
                                    printf("\tifindex: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_REFCNT:
                                    printf("\trefcnt: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_REACHABLE_TIME:
                                    printf("\treachable_time: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_BASE_REACHABLE_TIME:
                                    printf("\tbase_reachable_time: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_RETRANS_TIME:
                                    printf("\tretrans_time: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_GC_STALETIME:
                                    printf("\tgc_staletime: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_DELAY_PROBE_TIME:
                                    printf("\tdelay_probe_time: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_QUEUE_LEN:
                                    printf("\tqueue_len: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_APP_PROBES:
                                    printf("\tapp_probes: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_UCAST_PROBES:
                                    printf("\tucast_probes: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_MCAST_PROBES:
                                    printf("\tmcast_probes: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_ANYCAST_DELAY:
                                    printf("\tanycast_delay: %lld\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_PROXY_DELAY:
                                    printf("\tproxy_delay: %d\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_PROXY_QLEN:
                                    printf("\tproxy_qlen: %d\n", *(uint32_t*)RTA_DATA(parm_rta));
                                    break;
                                case NDTPA_LOCKTIME:
                                    printf("\tlocktime: %d\n", *(uint64_t*)RTA_DATA(parm_rta));
                                    break;
                            }
                        }
                    }
                    break;
                case NDTA_STATS:
                    {
                        struct ndt_stats *stats = (struct ndt_stats*)RTA_DATA(rta);
                        printf("STATS:\n");
                        printf("\tallocs: %d\n", stats->ndts_allocs);
                        printf("\tdestorys: %d\n", stats->ndts_destroys);
                        printf("\tgrow: %d\n", stats->ndts_hash_grows);
                        printf("\tres_failed: %d\n", stats->ndts_res_failed);
                        printf("\tlookups %d\n", stats->ndts_lookups);
                        printf("\thits: %d\n", stats->ndts_hits);
                        printf("\trcv_probes_mcast: %d\n", stats->ndts_rcv_probes_mcast);
                        printf("\trcv_probes_ucast: %d\n", stats->ndts_rcv_probes_ucast);
                        printf("\tperiodic_gc_runs: %d\n", stats->ndts_periodic_gc_runs);
                        printf("\tforced_gc_runs: %d\n", stats->ndts_forced_gc_runs);
                    }
                    break;
                case NDTA_GC_INTERVAL:
                    printf("GC_INTERVAL %lld\n", *(uint64_t*)RTA_DATA(rta));
                    break;
            }
        }
    }
}

void main() {
    srand(time(NULL));
    seq = rand();

    int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
    if ( sock == -1 ) {
        perror("sock error\n");
        exit(1);
    }

    struct {
        struct nlmsghdr nh;
        struct rtgenmsg rt;
    } h;

    struct sockaddr_nl nl;
    memset(&nl, 0, sizeof(nl));
    nl.nl_family = AF_NETLINK;

    if ( bind(sock, (struct sockaddr*)&nl, sizeof(nl)) == -1 ) {
        perror("bind error\n");
        exit(1);
    }

    memset(&h, 0, sizeof(h));
    h.nh.nlmsg_type  = RTM_GETNEIGHTBL;
    h.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
    h.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(h.rt));
    h.nh.nlmsg_seq   = seq;
    h.rt.rtgen_family = AF_INET;

    int n = write(sock, &h, sizeof(h));
    if ( n <= 0 ) {
        perror("write error\n");
        exit(1);
    }
    msg_receive(sock);
}

出力はこんな感じ

NAME: arp_cache
GC_INTERVAL 10000
THRESH1 128
THRESH2 512
THRESH3 1024
CONFIG:
        key_len: 4
        entry_size: 260
        entries: 3
        last_flush: 27997298
        last_rand: 63783
        hash_rnd: 398501669
        hash_mask: 7
        hash_chain_gc: 0
        proxy_qlen: 0
STATS:
        allocs: 49
        destorys: 46
        grow: 0
        res_failed: 25
        lookups 61661
        hits: 48360
        rcv_probes_mcast: 0
        rcv_probes_ucast: 0
        periodic_gc_runs: 2274
        forced_gc_runs: 0
PARAMS:
        refcnt: 1
        queue_len: 3
        proxy_qlen: 64
        app_probes: 0
        ucast_probes: 3
        mcast_probes: 3
        reachable_time: 18336
        base_reachable_time: 20000
        gc_staletime: 60000
        delay_probe_time: 5000
        retrans_time: 1000
        anycast_delay: 1000
        proxy_delay: 800
        locktime: 1000