1 /* vi: set sw=4 ts=4: */
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License
5 * as published by the Free Software Foundation; either version
6 * 2 of the License, or (at your option) any later version.
8 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
11 #include <sys/socket.h>
15 #include "libnetlink.h"
17 void FAST_FUNC
xrtnl_open(struct rtnl_handle
*rth
/*, unsigned subscriptions*/)
21 memset(rth
, 0, sizeof(*rth
));
22 rth
->fd
= xsocket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
23 rth
->local
.nl_family
= AF_NETLINK
;
24 /*rth->local.nl_groups = subscriptions;*/
26 xbind(rth
->fd
, (struct sockaddr
*)&rth
->local
, sizeof(rth
->local
));
27 addr_len
= sizeof(rth
->local
);
28 getsockname(rth
->fd
, (struct sockaddr
*)&rth
->local
, &addr_len
);
31 if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
32 bb_perror_msg_and_die("getsockname");
33 if (addr_len != sizeof(rth->local))
34 bb_error_msg_and_die("wrong address length %d", addr_len);
35 if (rth->local.nl_family != AF_NETLINK)
36 bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
38 rth
->seq
= time(NULL
);
41 int FAST_FUNC
xrtnl_wilddump_request(struct rtnl_handle
*rth
, int family
, int type
)
48 req
.nlh
.nlmsg_len
= sizeof(req
);
49 req
.nlh
.nlmsg_type
= type
;
50 req
.nlh
.nlmsg_flags
= NLM_F_ROOT
|NLM_F_MATCH
|NLM_F_REQUEST
;
51 req
.nlh
.nlmsg_pid
= 0;
52 req
.nlh
.nlmsg_seq
= rth
->dump
= ++rth
->seq
;
53 req
.g
.rtgen_family
= family
;
55 return rtnl_send(rth
, (void*)&req
, sizeof(req
));
58 //TODO: pass rth->fd instead of full rth?
59 int FAST_FUNC
rtnl_send(struct rtnl_handle
*rth
, char *buf
, int len
)
61 struct sockaddr_nl nladdr
;
63 memset(&nladdr
, 0, sizeof(nladdr
));
64 nladdr
.nl_family
= AF_NETLINK
;
66 return xsendto(rth
->fd
, buf
, len
, (struct sockaddr
*)&nladdr
, sizeof(nladdr
));
69 int FAST_FUNC
rtnl_dump_request(struct rtnl_handle
*rth
, int type
, void *req
, int len
)
72 struct sockaddr_nl nladdr
;
73 struct iovec iov
[2] = { { &nlh
, sizeof(nlh
) }, { req
, len
} };
75 (void*)&nladdr
, sizeof(nladdr
),
81 memset(&nladdr
, 0, sizeof(nladdr
));
82 nladdr
.nl_family
= AF_NETLINK
;
84 nlh
.nlmsg_len
= NLMSG_LENGTH(len
);
85 nlh
.nlmsg_type
= type
;
86 nlh
.nlmsg_flags
= NLM_F_ROOT
|NLM_F_MATCH
|NLM_F_REQUEST
;
88 nlh
.nlmsg_seq
= rth
->dump
= ++rth
->seq
;
90 return sendmsg(rth
->fd
, &msg
, 0);
93 static int rtnl_dump_filter(struct rtnl_handle
*rth
,
94 int (*filter
)(const struct sockaddr_nl
*, struct nlmsghdr
*n
, void *) FAST_FUNC
,
96 int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
100 char *buf
= xmalloc(8*1024); /* avoid big stack buffer */
101 struct sockaddr_nl nladdr
;
102 struct iovec iov
= { buf
, 8*1024 };
108 struct msghdr msg
= {
109 (void*)&nladdr
, sizeof(nladdr
),
115 status
= recvmsg(rth
->fd
, &msg
, 0);
120 bb_perror_msg("OVERRUN");
124 bb_error_msg("EOF on netlink");
127 if (msg
.msg_namelen
!= sizeof(nladdr
)) {
128 bb_error_msg_and_die("sender address length == %d", msg
.msg_namelen
);
131 h
= (struct nlmsghdr
*)buf
;
132 while (NLMSG_OK(h
, status
)) {
135 if (nladdr
.nl_pid
!= 0 ||
136 h
->nlmsg_pid
!= rth
->local
.nl_pid
||
137 h
->nlmsg_seq
!= rth
->dump
140 // err = junk(&nladdr, h, arg2);
149 if (h
->nlmsg_type
== NLMSG_DONE
) {
152 if (h
->nlmsg_type
== NLMSG_ERROR
) {
153 struct nlmsgerr
*l_err
= (struct nlmsgerr
*)NLMSG_DATA(h
);
154 if (h
->nlmsg_len
< NLMSG_LENGTH(sizeof(struct nlmsgerr
))) {
155 bb_error_msg("ERROR truncated");
157 errno
= -l_err
->error
;
158 bb_perror_msg("RTNETLINK answers");
162 err
= filter(&nladdr
, h
, arg1
);
169 h
= NLMSG_NEXT(h
, status
);
171 if (msg
.msg_flags
& MSG_TRUNC
) {
172 bb_error_msg("message truncated");
176 bb_error_msg_and_die("remnant of size %d!", status
);
186 int FAST_FUNC
xrtnl_dump_filter(struct rtnl_handle
*rth
,
187 int (*filter
)(const struct sockaddr_nl
*, struct nlmsghdr
*, void *) FAST_FUNC
,
190 int ret
= rtnl_dump_filter(rth
, filter
, arg1
/*, NULL, NULL*/);
192 bb_error_msg_and_die("dump terminated");
196 int FAST_FUNC
rtnl_talk(struct rtnl_handle
*rtnl
, struct nlmsghdr
*n
,
197 pid_t peer
, unsigned groups
,
198 struct nlmsghdr
*answer
,
199 int (*junk
)(struct sockaddr_nl
*, struct nlmsghdr
*, void *),
202 /* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
211 struct sockaddr_nl nladdr
;
212 struct iovec iov
= { (void*)n
, n
->nlmsg_len
};
213 char *buf
= xmalloc(8*1024); /* avoid big stack buffer */
214 struct msghdr msg
= {
215 (void*)&nladdr
, sizeof(nladdr
),
221 memset(&nladdr
, 0, sizeof(nladdr
));
222 nladdr
.nl_family
= AF_NETLINK
;
223 // nladdr.nl_pid = peer;
224 // nladdr.nl_groups = groups;
226 n
->nlmsg_seq
= seq
= ++rtnl
->seq
;
227 if (answer
== NULL
) {
228 n
->nlmsg_flags
|= NLM_F_ACK
;
230 status
= sendmsg(rtnl
->fd
, &msg
, 0);
233 bb_perror_msg("can't talk to rtnetlink");
240 iov
.iov_len
= 8*1024;
241 status
= recvmsg(rtnl
->fd
, &msg
, 0);
244 if (errno
== EINTR
) {
247 bb_perror_msg("OVERRUN");
251 bb_error_msg("EOF on netlink");
254 if (msg
.msg_namelen
!= sizeof(nladdr
)) {
255 bb_error_msg_and_die("sender address length == %d", msg
.msg_namelen
);
257 for (h
= (struct nlmsghdr
*)buf
; status
>= (int)sizeof(*h
); ) {
259 int len
= h
->nlmsg_len
;
260 int l
= len
- sizeof(*h
);
262 if (l
< 0 || len
> status
) {
263 if (msg
.msg_flags
& MSG_TRUNC
) {
264 bb_error_msg("truncated message");
267 bb_error_msg_and_die("malformed message: len=%d!", len
);
270 if (nladdr
.nl_pid
!= peer
||
271 h
->nlmsg_pid
!= rtnl
->local
.nl_pid
||
275 // l_err = junk(&nladdr, h, jarg);
284 if (h
->nlmsg_type
== NLMSG_ERROR
) {
285 struct nlmsgerr
*err
= (struct nlmsgerr
*)NLMSG_DATA(h
);
286 if (l
< (int)sizeof(struct nlmsgerr
)) {
287 bb_error_msg("ERROR truncated");
289 errno
= - err
->error
;
292 memcpy(answer
, h
, h
->nlmsg_len
);
296 bb_perror_msg("RTNETLINK answers");
301 memcpy(answer
, h
, h
->nlmsg_len
);
305 bb_error_msg("unexpected reply!");
307 status
-= NLMSG_ALIGN(len
);
308 h
= (struct nlmsghdr
*)((char*)h
+ NLMSG_ALIGN(len
));
310 if (msg
.msg_flags
& MSG_TRUNC
) {
311 bb_error_msg("message truncated");
315 bb_error_msg_and_die("remnant of size %d!", status
);
325 int FAST_FUNC
addattr32(struct nlmsghdr
*n
, int maxlen
, int type
, uint32_t data
)
327 int len
= RTA_LENGTH(4);
330 if ((int)(NLMSG_ALIGN(n
->nlmsg_len
) + len
) > maxlen
) {
333 rta
= (struct rtattr
*)(((char*)n
) + NLMSG_ALIGN(n
->nlmsg_len
));
334 rta
->rta_type
= type
;
336 move_to_unaligned32(RTA_DATA(rta
), data
);
337 n
->nlmsg_len
= NLMSG_ALIGN(n
->nlmsg_len
) + len
;
341 int FAST_FUNC
addattr_l(struct nlmsghdr
*n
, int maxlen
, int type
, void *data
, int alen
)
343 int len
= RTA_LENGTH(alen
);
346 if ((int)(NLMSG_ALIGN(n
->nlmsg_len
) + len
) > maxlen
) {
349 rta
= (struct rtattr
*)(((char*)n
) + NLMSG_ALIGN(n
->nlmsg_len
));
350 rta
->rta_type
= type
;
352 memcpy(RTA_DATA(rta
), data
, alen
);
353 n
->nlmsg_len
= NLMSG_ALIGN(n
->nlmsg_len
) + len
;
357 int FAST_FUNC
rta_addattr32(struct rtattr
*rta
, int maxlen
, int type
, uint32_t data
)
359 int len
= RTA_LENGTH(4);
360 struct rtattr
*subrta
;
362 if (RTA_ALIGN(rta
->rta_len
) + len
> maxlen
) {
365 subrta
= (struct rtattr
*)(((char*)rta
) + RTA_ALIGN(rta
->rta_len
));
366 subrta
->rta_type
= type
;
367 subrta
->rta_len
= len
;
368 move_to_unaligned32(RTA_DATA(subrta
), data
);
369 rta
->rta_len
= NLMSG_ALIGN(rta
->rta_len
) + len
;
373 int FAST_FUNC
rta_addattr_l(struct rtattr
*rta
, int maxlen
, int type
, void *data
, int alen
)
375 struct rtattr
*subrta
;
376 int len
= RTA_LENGTH(alen
);
378 if (RTA_ALIGN(rta
->rta_len
) + len
> maxlen
) {
381 subrta
= (struct rtattr
*)(((char*)rta
) + RTA_ALIGN(rta
->rta_len
));
382 subrta
->rta_type
= type
;
383 subrta
->rta_len
= len
;
384 memcpy(RTA_DATA(subrta
), data
, alen
);
385 rta
->rta_len
= NLMSG_ALIGN(rta
->rta_len
) + len
;
390 void FAST_FUNC
parse_rtattr(struct rtattr
*tb
[], int max
, struct rtattr
*rta
, int len
)
392 while (RTA_OK(rta
, len
)) {
393 if (rta
->rta_type
<= max
) {
394 tb
[rta
->rta_type
] = rta
;
396 rta
= RTA_NEXT(rta
, len
);
399 bb_error_msg("deficit %d, rta_len=%d!", len
, rta
->rta_len
);