2 * Copyright (C) 2008 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include <sys/socket.h>
17 #include <linux/netlink.h>
18 #include <linux/rtnetlink.h>
22 #include "kernel_netlink_shared.h"
24 #include <utils/debug.h>
25 #include <threading/mutex.h>
27 typedef struct private_netlink_socket_t private_netlink_socket_t
;
30 * Private variables and functions of netlink_socket_t class.
32 struct private_netlink_socket_t
{
34 * public part of the netlink_socket_t object.
36 netlink_socket_t
public;
39 * mutex to lock access to netlink socket
44 * current sequence number for netlink request
49 * netlink socket protocol
60 * Imported from kernel_netlink_ipsec.c
62 extern enum_name_t
*xfrm_msg_names
;
64 METHOD(netlink_socket_t
, netlink_send
, status_t
,
65 private_netlink_socket_t
*this, struct nlmsghdr
*in
, struct nlmsghdr
**out
,
69 struct sockaddr_nl addr
;
70 chunk_t result
= chunk_empty
, tmp
;
71 struct nlmsghdr
*msg
, peek
;
73 this->mutex
->lock(this->mutex
);
75 in
->nlmsg_seq
= ++this->seq
;
76 in
->nlmsg_pid
= getpid();
78 memset(&addr
, 0, sizeof(addr
));
79 addr
.nl_family
= AF_NETLINK
;
83 if (this->protocol
== NETLINK_XFRM
)
85 chunk_t in_chunk
= { (u_char
*)in
, in
->nlmsg_len
};
87 DBG3(DBG_KNL
, "sending %N: %B", xfrm_msg_names
, in
->nlmsg_type
, &in_chunk
);
92 len
= sendto(this->socket
, in
, in
->nlmsg_len
, 0,
93 (struct sockaddr
*)&addr
, sizeof(addr
));
95 if (len
!= in
->nlmsg_len
)
99 /* interrupted, try again */
102 this->mutex
->unlock(this->mutex
);
103 DBG1(DBG_KNL
, "error sending to netlink socket: %s", strerror(errno
));
112 tmp
.len
= sizeof(buf
);
114 msg
= (struct nlmsghdr
*)tmp
.ptr
;
116 memset(&addr
, 0, sizeof(addr
));
117 addr
.nl_family
= AF_NETLINK
;
118 addr
.nl_pid
= getpid();
120 addr_len
= sizeof(addr
);
122 len
= recvfrom(this->socket
, tmp
.ptr
, tmp
.len
, 0,
123 (struct sockaddr
*)&addr
, &addr_len
);
129 DBG1(DBG_KNL
, "got interrupted");
130 /* interrupted, try again */
133 DBG1(DBG_KNL
, "error reading from netlink socket: %s", strerror(errno
));
134 this->mutex
->unlock(this->mutex
);
138 if (!NLMSG_OK(msg
, len
))
140 DBG1(DBG_KNL
, "received corrupted netlink message");
141 this->mutex
->unlock(this->mutex
);
145 if (msg
->nlmsg_seq
!= this->seq
)
147 DBG1(DBG_KNL
, "received invalid netlink sequence number");
148 if (msg
->nlmsg_seq
< this->seq
)
152 this->mutex
->unlock(this->mutex
);
158 result
.ptr
= realloc(result
.ptr
, result
.len
+ tmp
.len
);
159 memcpy(result
.ptr
+ result
.len
, tmp
.ptr
, tmp
.len
);
160 result
.len
+= tmp
.len
;
162 /* NLM_F_MULTI flag does not seem to be set correctly, we use sequence
163 * numbers to detect multi header messages */
164 len
= recvfrom(this->socket
, &peek
, sizeof(peek
), MSG_PEEK
| MSG_DONTWAIT
,
165 (struct sockaddr
*)&addr
, &addr_len
);
167 if (len
== sizeof(peek
) && peek
.nlmsg_seq
== this->seq
)
169 /* seems to be multipart */
175 *out_len
= result
.len
;
176 *out
= (struct nlmsghdr
*)result
.ptr
;
178 this->mutex
->unlock(this->mutex
);
183 METHOD(netlink_socket_t
, netlink_send_ack
, status_t
,
184 private_netlink_socket_t
*this, struct nlmsghdr
*in
)
186 struct nlmsghdr
*out
, *hdr
;
189 if (netlink_send(this, in
, &out
, &len
) != SUCCESS
)
194 while (NLMSG_OK(hdr
, len
))
196 switch (hdr
->nlmsg_type
)
200 struct nlmsgerr
* err
= (struct nlmsgerr
*)NLMSG_DATA(hdr
);
204 if (-err
->error
== EEXIST
)
205 { /* do not report existing routes */
209 if (-err
->error
== ESRCH
)
210 { /* do not report missing entries */
214 DBG1(DBG_KNL
, "received netlink error: %s (%d)",
215 strerror(-err
->error
), -err
->error
);
223 hdr
= NLMSG_NEXT(hdr
, len
);
230 DBG1(DBG_KNL
, "netlink request not acknowledged");
235 METHOD(netlink_socket_t
, destroy
, void,
236 private_netlink_socket_t
*this)
238 if (this->socket
> 0)
242 this->mutex
->destroy(this->mutex
);
247 * Described in header.
249 netlink_socket_t
*netlink_socket_create(int protocol
)
251 private_netlink_socket_t
*this;
252 struct sockaddr_nl addr
;
256 .send
= _netlink_send
,
257 .send_ack
= _netlink_send_ack
,
261 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
262 .protocol
= protocol
,
265 memset(&addr
, 0, sizeof(addr
));
266 addr
.nl_family
= AF_NETLINK
;
268 this->socket
= socket(AF_NETLINK
, SOCK_RAW
, protocol
);
269 if (this->socket
< 0)
271 DBG1(DBG_KNL
, "unable to create netlink socket");
277 if (bind(this->socket
, (struct sockaddr
*)&addr
, sizeof(addr
)))
279 DBG1(DBG_KNL
, "unable to bind netlink socket");
284 return &this->public;
288 * Described in header.
290 void netlink_add_attribute(struct nlmsghdr
*hdr
, int rta_type
, chunk_t data
,
295 if (NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_LENGTH(data
.len
) > buflen
)
297 DBG1(DBG_KNL
, "unable to add attribute, buffer too small");
301 rta
= (struct rtattr
*)(((char*)hdr
) + NLMSG_ALIGN(hdr
->nlmsg_len
));
302 rta
->rta_type
= rta_type
;
303 rta
->rta_len
= RTA_LENGTH(data
.len
);
304 memcpy(RTA_DATA(rta
), data
.ptr
, data
.len
);
305 hdr
->nlmsg_len
= NLMSG_ALIGN(hdr
->nlmsg_len
) + rta
->rta_len
;
309 * Described in header.
311 void* netlink_reserve(struct nlmsghdr
*hdr
, int buflen
, int type
, int len
)
315 if (NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_LENGTH(len
) > buflen
)
317 DBG1(DBG_KNL
, "unable to add attribute, buffer too small");
321 rta
= ((void*)hdr
) + NLMSG_ALIGN(hdr
->nlmsg_len
);
322 rta
->rta_type
= type
;
323 rta
->rta_len
= RTA_LENGTH(len
);
324 hdr
->nlmsg_len
= NLMSG_ALIGN(hdr
->nlmsg_len
) + rta
->rta_len
;
326 return RTA_DATA(rta
);