root/luci2/libunl/io.c @ 6262

Revision 6262, 4.7 KB (checked in by Cyrus, 3 years ago)

Adding libunl

Line 
1/**
2 *   unl - Minimalistic netlink library
3 *   Copyright (C) 2010 Steven Barth <steven@midlink.org>
4 *   Copyright (C) 2010 John Crispin <blogic@openwrt.org>
5 *
6 *   This program is free software; you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation; either version 2 of the License, or
9 *   (at your option) any later version.
10 *
11 *   This program is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *   GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with this program; if not, write to the Free Software
18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <stdint.h>
23#include <asm/types.h>
24#include <sys/socket.h>
25#include <string.h>
26#include <time.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <errno.h>
30
31#include "unl/io.h"
32#include "unl/msg.h"
33
34
35int unl_open(struct unl *hndl, int proto, const struct sockaddr_nl *peer) {
36    const struct sockaddr_nl kernel = {.nl_family = AF_NETLINK};
37    if (!peer) peer = &kernel;
38
39    memset(hndl, 0, sizeof(*hndl));
40    if ((hndl->fd = socket(AF_NETLINK, SOCK_RAW, proto)) < 0) return -1;
41    fcntl(hndl->fd, F_SETFD, fcntl(hndl->fd, F_GETFD) | FD_CLOEXEC);
42
43    struct sockaddr_nl local = { .nl_family = AF_NETLINK };
44
45    if (bind(hndl->fd, (struct sockaddr*)&local, sizeof(hndl->addr))
46    || connect(hndl->fd, (struct sockaddr*)peer, sizeof(*peer))) {
47        close(hndl->fd);
48        return -1;
49    }
50
51    return 0;
52}
53
54int unl_subscribe(struct unl *hndl, uint32_t group) {
55    return setsockopt(hndl->fd, SOL_NETLINK,
56        NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
57}
58
59int unl_unsubscribe(struct unl *hndl, uint32_t group) {
60    return setsockopt(hndl->fd, SOL_NETLINK,
61        NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
62}
63
64int unl_close(struct unl *hndl) {
65    int stat = close(hndl->fd);
66    if (!stat)
67        hndl->fd = -1;
68    return stat;
69}
70
71
72
73/* IO functions */
74
75// Flush it
76void unl_flush(struct unl *hndl) {
77    uint8_t buf[32];
78    struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
79    struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
80    while (recvmsg(hndl->fd, &msg, MSG_DONTWAIT) > 0);
81}
82
83// Sends a request (return a message sequence id > 0 on success, -1 on fail)
84int unl_request(struct unl *hndl, struct nlmsghdr *nlm) {
85    struct iovec iov = { .iov_base = nlm, .iov_len = nlm->nlmsg_len };
86    struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
87    nlm->nlmsg_flags |= NLM_F_REQUEST;
88    nlm->nlmsg_pid = 0;
89    nlm->nlmsg_seq = ++hndl->seq;
90    ssize_t txed = sendmsg(hndl->fd, &msg, 0);
91    if (txed == nlm->nlmsg_len) {
92        hndl->status = UNL_STATUS_MORE;
93        hndl->buffered = 0;
94        return 0;
95    } else if (txed > 0) {
96        errno = EPIPE;
97    }
98    hndl->status = UNL_STATUS_READY;
99    return -1;
100}
101
102// Simple receive
103ssize_t unl_receive(struct unl *hndl, void *buf, size_t buflen) {
104    struct iovec iov = { .iov_base = buf, .iov_len = buflen };
105    struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
106    ssize_t len = recvmsg(hndl->fd, &msg, 0);
107    if (msg.msg_flags & MSG_TRUNC) {
108        errno = ENOBUFS;
109        len = -1;
110    }
111    return len;
112}
113
114
115struct nlmsghdr* unl_response
116(struct unl *hndl, void *buf, size_t len, int *errcode) {
117    if (hndl->status != UNL_STATUS_MORE) return 0;
118    for (;; hndl->current = NLMSG_NEXT(hndl->current, hndl->buffered)) {
119        while (!NLMSG_OK(hndl->current, hndl->buffered)) {
120            ssize_t rxed = unl_receive(hndl, buf, len);
121            if (rxed <= 0) {
122                if (errcode) *errcode = -errno;
123                return NULL; // IO error
124            }
125            hndl->buffered = rxed;
126            hndl->current = (struct nlmsghdr*)buf;
127        }
128        struct nlmsghdr *c = hndl->current;
129        if (c->nlmsg_seq != hndl->seq) continue; // not for us...
130        if (c->nlmsg_type == NLMSG_ERROR) {
131            if (NLMSG_PAYLOAD(c, sizeof(struct nlmsgerr)) < 0) {
132                errno = EPROTO;
133            } else { // regular error message
134                errno = -((struct nlmsgerr*)NLMSG_DATA(c))->error;
135            }
136            if (errno) { // ACK packages are error packages with error 0
137                if (errcode) *errcode = -errno;
138                return NULL;
139            }
140        }
141        if (c->nlmsg_type == NLMSG_DONE) {
142            hndl->status = UNL_STATUS_READY;
143            if (errcode) *errcode = 0;
144            return NULL;
145        } else {
146            if (!(c->nlmsg_flags & NLM_F_MULTI))
147                hndl->status = UNL_STATUS_READY;
148            hndl->current = NLMSG_NEXT(c, hndl->buffered);
149            if (errcode) *errcode = 0;
150            return c;
151        }
152    }
153}
154
155
156int unl_call(struct unl *hndl, struct nlmsghdr *nlm) {
157    uint8_t buffer[UNL_BUFLEN];
158    nlm->nlmsg_flags |= NLM_F_ACK; // We need some reply
159    int status = unl_request(hndl, nlm);
160    if (!status)
161        unl_response(hndl, buffer, sizeof(buffer), &status);
162    else
163        status = -errno;
164    return status;
165}
Note: See TracBrowser for help on using the browser.