totofugaのブログ

ネットワークとかc言語、perlの話。

カーネルからユーザ空間にnetlinkブロードキャスト

iptablesのULOGを調べててカーネル空間にnetlinkにブロードキャストを送る方法が気になったので書いてみた

カーネル空間のプログラム

#include <linux/socket.h>                                                     
#include <net/sock.h>                                                         
                                                                              
static struct timer_list my_timer;                                            
static struct sock *sock;                                                     
static int seq;                                                               
                                                                              
MODULE_LICENSE("GPL");                                                        
                                                                              
void my_timer_callback( unsigned long data ) {                                
    char word[] = "hello world!";                                             
    struct sk_buff *skb;                                                      
    int type = 0;                                                             
    struct nlmsghdr *nlh;                                                     
    char *d;                                                                  
    int ret;                                                                  
                                                                              
    printk("timeout\n");                                                      
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(200));                    
                                                                              
                                                                              
    skb = alloc_skb(1024, GFP_ATOMIC);                                        
    if ( !skb ) {                                                             
        printk("error: skb alloc\n");                                         
        return;                                                               
    }                                                                         
                                                                              
    nlh = NLMSG_PUT(skb, 0, ++seq, type, strlen(word));                       
    d = NLMSG_DATA(nlh);                                                      
    memcpy(d, word, strlen(word));                                            
                                                                              
    ret = netlink_broadcast(sock, skb, 0, 5, GFP_ATOMIC);                                 
                                                                              
    return;                                                                   
                                                                              
nlmsg_failure:                                                                
    printk("error:NLMSG_PUT\n");                                              
    return;                                                                   
}                                                                             
                                                                              
int myrtnl_init(void) {                                                       
                                                                              
    sock = netlink_kernel_create(&init_net, 20, 32, NULL, NULL, THIS_MODULE); 
    if ( !sock ) {                                                            
        printk("error: netlink kernel create\n");                             
        return 1;                                                             
    }                                                                         
                                                                              
    setup_timer(&my_timer, my_timer_callback, 0);                             
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(200));                    
    return 0;                                                                 
}                                                                             
                                                                              
void myrtnl_exit(void) {                                                      
    del_timer(&my_timer);                                                     
    netlink_kernel_release(sock);                                             
}                                                                             
                                                                              
module_init(myrtnl_init);                                                     
module_exit(myrtnl_exit);                                                     

200ミリ秒ごとにNETLINKにブロードキャストを送るプログラム

cat /proc/net/netlink で空いてるをとりあえず使ってみた。(ルールは今度ちゃんと調べる)

netlink_broadcastにsk_buffを渡せば作れる。

NLMSG_PUTというショートカットメソッドも用意されている。

ユーザ空間のプログラム

#include <stdio.h>                                                               
#include <libnetlink.h>                                                          
#include <stdlib.h>                                                              
#include <string.h>                                                              
                                                                                 
int listen_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg) {
    char buf[256] = {0};                                                         
    memcpy(buf, NLMSG_DATA(n), 12);                                              
    printf("data: %s\n", buf);                                                   
    return 0;                                                                    
}                                                                                
void main() {                                                                    
    struct rtnl_handle rh;                                                       
    int n = 5;                                                                   
    if ( rtnl_open_byproto(&rh, 1 << (n - 1), 20) != 0 ) {                       
        perror("open error\n");                                                  
        exit(1);                                                                 
    }                                                                            
                                                                                 
    printf("listen start\n");                                                    
    rtnl_listen(&rh, listen_handler, NULL);                                      
}                                                                                

libnetlinkを使った受信

ユーザ空間のグループ指定はビットシフトになるので注意

出力結果

[root@localhost rtnetlink]# ./a.out 
listen start                        
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!                  
data: hello world!