上一篇《Netlink 监听 XFRM 状态消息 》学习了抓包
和解包
,本文来学习构造包
和发包
监听是一种被动的方式,实现增删改查(also known as CRUD)需要主动与内核交互,也就是发包给内核“指示”。
下面详细介绍了struct nlmsghdr
和 sendmsg()
,在此基础上实现了SAD导出功能。
struct nlmsghdr {
__u32 nlmsg_len ; /* Length of message including header. */
__u16 nlmsg_type ; /* Type of message content. */
__u16 nlmsg_flags ; /* Additional flags. */
__u32 nlmsg_seq ; /* Sequence number. */
__u32 nlmsg_pid ; /* Sender port ID. */
};
nlmsg_len
数据总长度,包含头部
nlmsg_type
netlink message 类型,这个字段在构造和解析时都很常用,每一种netlink_family属于一大类。
NETLINK_ROUTE 对应的是 RTN_XXX的类型
NETLINK_XFRM 对应的是 XFRM_XXX的类型
如果要获取内核中SAD信息,需要在nlmsghdr中指定type = XFRM_MSG_GETSA
nlmsg_flags
该字段比较生疏,调试时,发送的请求一直得不到正确的响应数据,就是因为nlmsg_flags设置错误。
请求导出SAD, NLM_F_REQUEST
是必须要设置的,但是还不够,还要设置 NLM_F_DUMP
。
所有flags参考
Standard flag bits in nlmsg_flags
----------------------------------------------------------
NLM_F_REQUEST Must be set on all request messages.
NLM_F_MULTI The message is part of a multipart mes-
sage terminated by NLMSG_DONE.
NLM_F_ACK Request for an acknowledgment on success.
NLM_F_ECHO Echo this request.
Additional flag bits for GET requests
--------------------------------------------------------------------
NLM_F_ROOT Return the complete table instead of a single entry.
NLM_F_MATCH Return all entries matching criteria passed in mes-
sage content. Not implemented yet.
NLM_F_ATOMIC Return an atomic snapshot of the table.
NLM_F_DUMP Convenience macro; equivalent to
( NLM_F_ROOT|NLM_F_MATCH) .
Note that NLM_F_ATOMIC requires the CAP_NET_ADMIN capability or an
effective UID of 0.
Additional flag bits for NEW requests
------------------------------------------------------------
NLM_F_REPLACE Replace existing matching object.
NLM_F_EXCL Don't replace if the object already exists.
NLM_F_CREATE Create object if it doesn' t already exist.
NLM_F_APPEND Add to the end of the object list.
from: http://stuff.onse.fi/man?program= netlink§ion= 7
##调用sendmsg()
这里发包调用的是 sendmsg
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
它和send, sendto的区别在于
For send() and sendto(), the message is found in buf and has length len. For sendmsg(), the message is pointed to by the elements of the array msg.msg_iov. The sendmsg() call also allows sending ancillary data (also known as control information). ——http://linux.die.net/man/2/sendmsg
一个关键的数据结构 msghdr
/* Structure describing messages sent by
`sendmsg' and received by `recvmsg'. */
struct msghdr
{
void * msg_name ; /* Address to send to/receive from. */
socklen_t msg_namelen ; /* Length of address data. */
struct iovec * msg_iov ; /* Vector of data to send/receive into. */
size_t msg_iovlen ; /* Number of elements in the vector. */
void * msg_control ; /* Ancillary data (eg BSD filedesc passing). */
size_t msg_controllen ; /* Ancillary data buffer length.
!! The type should be socklen_t but the
definition of the kernel is incompatible
with this. */
int msg_flags ; /* Flags on received message. */
};
struct iovec
{
void * iov_base ; /* BSD uses caddr_t (1003.1g requires void *) */
__kernel_size_t iov_len ; /* Must be size_t (1003.1g) */
};
iovec 作用?
uses the iovec structure for scatter/gather I/O.
什么是 scatter/gather I/O ?
In computing, vectored I/O, also known as scatter/gather I/O, is a method of input and output by which a single procedure-call sequentially writes data from multiple buffers to a single data stream or reads data from a data stream to multiple buffers. (维基,含有C语言实例)——https://en.wikipedia.org/wiki/Vectored_I/O
示例程序
参考ip xfrm state list
实现源码,实现了一个发包查询xfrm state (SAD)
功能。
/*
gcc xfrm_list.c `pkg-config --cflags --libs libnl-3.0 libnl-xfrm-3.0` -w -g
*/
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/xfrm.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/types.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define RTA_BUF_SIZE 2048
#define PFUNC() printf("[+%s]\n", __FUNCTION__)
#define NLMSG_TYPE(type) printf(#type " : %d\n", type)
#define zero(mem) memset(&mem, 0, sizeof(mem))
int parse_nlmsg ( struct nlmsghdr * , int msglen );
void parse_sa ( struct nlmsghdr * nlh );
void nlmsg_type_map ();
void dump_hex ( char * buf , int len );
void dump_nlmsg ( char * buf , int len );
int main ()
{
int sock ;
int err ;
int msglen ;
char buf [ 16384 ];
struct sockaddr_nl local ;
struct sockaddr_nl server ;
struct nlmsghdr * nlh ;
/*
request packet
| nlmsghdr | xfrm_usersa_id |
*/
struct {
struct nlmsghdr n ;
struct xfrm_usersa_id xsid ;
char buf [ RTA_BUF_SIZE ];
} req ;
req . n . nlmsg_len = NLMSG_LENGTH ( sizeof ( req . xsid ));
req . n . nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST ;
req . n . nlmsg_type = XFRM_MSG_GETSA ;
req . n . nlmsg_seq = time ( NULL );
req . n . nlmsg_pid = getpid ();
zero ( req . xsid );
req . xsid . family = AF_INET ;
req . xsid . proto = 0 ;
struct iovec iov = {
. iov_base = ( void * ) & req . n ,
. iov_len = req . n . nlmsg_len
};
struct msghdr msg = {
. msg_name = & server ,
. msg_namelen = sizeof ( server ),
. msg_iov = & iov ,
. msg_iovlen = 1 ,
};
zero ( server );
server . nl_family = AF_NETLINK ;
zero ( local );
local . nl_family = AF_NETLINK ;
local . nl_pid = getpid ();
sock = socket ( AF_NETLINK , SOCK_RAW , NETLINK_XFRM );
if ( sock < 0 ) {
perror ( "socket fail. \n " );
return - 1 ;
}
err = bind ( sock , ( struct sockaddr * ) & local , sizeof ( local ));
if ( err < 0 ) {
perror ( "bind fail. \n " );
return - 1 ;
}
err = sendmsg ( sock , & msg , 0 );
if ( err < 0 ) {
perror ( "sendmsg fail. \n " );
return - 1 ;
}
perror ( "sendmsg()" );
zero ( buf );
iov . iov_base = buf ;
iov . iov_len = sizeof ( buf );
while ( 1 ) {
msglen = recvmsg ( sock , & msg , 0 );
//dump_nlmsg(buf, msglen);
parse_nlmsg (( struct nlmsghdr * ) buf , msglen );
}
return 0 ;
}
void nlmsg_type_map ()
{
/*
xfrm message type list
libnl/include/linux-private/linux/xfrm.h :152
*/
printf ( "+----------<XFRM_MSG_TYPE : ID>--------+ \n " );
NLMSG_TYPE ( XFRM_MSG_NEWSA );
NLMSG_TYPE ( XFRM_MSG_DELSA );
NLMSG_TYPE ( XFRM_MSG_GETSA );
NLMSG_TYPE ( XFRM_MSG_NEWPOLICY );
NLMSG_TYPE ( XFRM_MSG_DELPOLICY );
NLMSG_TYPE ( XFRM_MSG_GETPOLICY );
NLMSG_TYPE ( XFRM_MSG_FLUSHSA );
NLMSG_TYPE ( XFRM_MSG_FLUSHPOLICY );
printf ( "+--------------------------------------+ \n " );
}
int parse_nlmsg ( struct nlmsghdr * nlh , int msglen )
{
PFUNC ();
//nlmsg_type_map();
for ( nlh ; NLMSG_OK ( nlh , msglen ); nlh = NLMSG_NEXT ( nlh , msglen )) {
switch ( nlh -> nlmsg_type ) {
case XFRM_MSG_NEWSA :
case XFRM_MSG_GETSA :
parse_sa ( nlh );
break ;
default:
printf ( "other nlmsg_type(%d). \n exit \n " ,
nlh -> nlmsg_type );
exit ( 1 );
}
}
return 0 ;
}
void parse_sa ( struct nlmsghdr * nlh )
{
参考 http : //onestraw.net/linux/netlink-xfrm-state-listen
}
void dump_nlmsg ( char * buf , int len )
{
int i ;
for ( i = 0 ; i < len ; i ++ ) {
if ( i % 16 == 0 ) {
printf ( " \n " );
}
printf ( "%02x " , buf [ i ] & 0xff );
}
printf ( " \n " );
}
void dump_hex ( char * buf , int len )
{
int i ;
printf ( "0x" );
for ( i = 0 ; i < len ; i ++ ) {
printf ( "%02x" , buf [ i ] & 0xff );
}
printf ( " \n " );
}
现在掌握了 SAD 导出和监听技术,这些都是setkey,ip xfrm 实现的基础,有时间再试验下 XFRM_MSG_NEWSA
, XFRM_MSG_DELSA
, XFRM_MSG_UPDSA
.
参考
iproute2: https://github.com/shemminger/iproute2/blob/master/ip/xfrm_state.c
《xfrm interface experiences..》第4页 http://vger.kernel.org/netconf2005_jamal.pdf
留下评论