summaryrefslogtreecommitdiffstats
path: root/src/nlapi.c
blob: 1f6e6906aba50a9f2a61cf97436c4d09688714b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <stdlib.h>

#include <libdaemon/dlog.h>

#include "nlapi.h"

int nlapi_fd = -1;

struct callback_info {
    nlapi_callback_t callback;
    void *userdata;
    struct callback_info * next;
};

struct callback_info *callbacks = NULL;

int nlapi_open(uint32_t groups) {
    struct sockaddr_nl addr;

    if ((nlapi_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
        fprintf(stderr, "socket(PF_NETLINK): %s\n", strerror(errno));
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_groups = groups;
    addr.nl_pid = getpid();

    if (bind(nlapi_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        close(nlapi_fd);
        fprintf(stderr, "bind(): %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int nlapi_work(int block) {

    assert(nlapi_fd >= 0);
    
    for (;;) {
        int bytes;
        char replybuf[1024];
        struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
        
        if ((bytes = recv(nlapi_fd, &replybuf, sizeof(replybuf), block ? 0 : MSG_DONTWAIT)) < 0) {

            if (errno == EAGAIN || errno == EINTR)
                return 0;
            
            daemon_log(LOG_ERR, "NLAPI: recv(): %s\n", strerror(errno));
            return -1;
        }

        for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
            struct callback_info *c;
            
            if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) {
                daemon_log(LOG_ERR, "NLAPI: Packet too small or truncated!\n");
                return -1;
            }
            
            for (c = callbacks; c; c = c->next)
                if (c->callback(p, c->userdata) < 0)
                    return -1;
        }

        if (block)
            break;
    }

    return 0;
}

void nlapi_close(void) {
    if (nlapi_fd >= 0)
        close(nlapi_fd);
    nlapi_fd = -1;


    while (callbacks) {
        struct callback_info *c = callbacks;
        callbacks = callbacks->next;
        free(c);
    }
}

int nlapi_register(int (*callback) (struct nlmsghdr *n, void *u), void *u) {
    struct callback_info *c;

    assert(callback);
    
    if (!(c = malloc(sizeof(struct callback_info)))) {
        daemon_log(LOG_ERR, "NLAPI: Not enough memory.\n");
        return -1;
    }

    c->callback = callback;
    c->userdata = u;

    c->next = callbacks;
    callbacks = c;
    return 0;
}