summaryrefslogtreecommitdiffstats
path: root/src/nlapi.c
blob: 52025baa0f4d1fea64817d252f141f2da3c433d7 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* $Id$ */

/*
 * This file is part of ifplugd.
 *
 * ifplugd is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * ifplugd is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ifplugd; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#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) {
        daemon_log(LOG_ERR, "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);
        daemon_log(LOG_ERR, "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) || (size_t) bytes < sizeof(struct nlmsghdr) || (size_t) 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;
}