2014-10-08 5 views
2

Задача, которую я пытаюсь достичь, на самом деле довольно проста (многоадресная передача «TEST» для демонстрантов userland), но модуль ядра не компилируется. Он останавливается с ошибкой:Netlink Multicast Kernel Group

passing argument 4 of ‘genlmsg_multicast_allns’ makes integer from pointer without a cast [enabled by default] 

Но разве это не должна быть группа многоадресной передачи, которую я определил?

Вот код для "очищения":

#include <linux/module.h> 
#include <net/sock.h> 
#include <linux/netlink.h> 
#include <linux/skbuff.h> 
#include <linux/string.h> 
#include <net/netlink.h> 
#include <net/genetlink.h> 

struct sock *nl_sk = NULL; 

static void daemon(void){ 
     struct sk_buff *skb; 
     void* msg_head; 
     unsigned char *msg; 

     struct genl_family my_genl_family = { 
       .id = GENL_ID_GENERATE, 
       .hdrsize = 0, 
       .name = "family_name", 
       .version = 1, 
       .maxattr = 5 
     }; 

     struct genl_multicast_group my_mc_group = { 
       .name = "mc_group", 
     }; 


     msg = "TEST"; 
     skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 

     msg_head = genlmsg_put(skb, 0, 0, &my_genl_family, 0, 21); 

     nla_put(skb, 0, sizeof(msg), msg); 

     genlmsg_end(skb, msg_head); 

     genlmsg_multicast_allns(&my_genl_family, skb, 0, my_mc_group, GFP_KERNEL); 

} 

static int __init hello_init(void) 
{ 
    printk("Entering: %s\n", __FUNCTION__); 

    printk(KERN_INFO "Calling main function with sockets\n"); 

     struct netlink_kernel_cfg cfg = { 
     .groups = 1, 
     .flags = NL_CFG_F_NONROOT_RECV, 
     }; 

     nl_sk = netlink_kernel_create(&init_net, NETLINK_GENERIC, &cfg); 


    daemon(); 

    return 0; 
} 

static void __exit hello_exit(void) 
{ 
    printk(KERN_INFO "exiting hello module\n"); 
    netlink_kernel_release(nl_sk); 
} 

module_init(hello_init); 
module_exit(hello_exit); 

MODULE_LICENSE("GPL"); 

Спасибо за вашу помощь.

EDIT

Это код на стороне клиента:

#include <netlink/netlink.h> 
#include <netlink/socket.h> 
#include <netlink/msg.h> 
#include <netlink/genl/genl.h> 
#include <linux/genetlink.h> 

/* 
* This function will be called for each valid netlink message received 
* in nl_recvmsgs_default() 
*/ 
static int my_func(struct nl_msg *msg, void *arg) 
{ 
     //struct nl_msg *nlmsg = nlmsg_alloc_size(GENL_HDRLEN+nla_total_size(sizeof(msg))+36); 

     printf("Test\n"); 

     return 0; 
} 

int main(){ 
     struct nl_sock *sk; 

     int gr_id; 

     /* Allocate a new socket */ 
     sk = nl_socket_alloc(); 

     /* 
     * Notifications do not use sequence numbers, disable sequence number 
     * checking. 
     */ 
     nl_socket_disable_seq_check(sk); 

     /* 
     * Define a callback function, which will be called for each notification 
     * received 
     */ 
     nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); 
     /* Connect to netlink generic protocol */ 
     nl_connect(sk, NETLINK_GENERIC); 

     gr_id = genl_family_get_id("family_name"); 

     /* Subscribe to link notifications group */ 
     nl_socket_add_memberships(sk, gr_id, 0); 

     /* 
     * Start receiving messages. The function nl_recvmsgs_default() will block 
     * until one or more netlink messages (notification) are received which 
     * will be passed on to my_func(). 
     */ 
     while (1){ 
       nl_recvmsgs_default(sk); 
     } 

     return 0; 
} 
+0

Вы должны использовать целое число, а не структура genl_multicast_group в этом вызове. Посмотрите, как это делается в другом месте ядра. Также обратите внимание, что это целое число имеет верхний предел, что-то вроде 32. –

+0

Спасибо за ваш комментарий. И да, верхний предел равен 32. Я попытаюсь скомпилировать исправленную версию. – user4122787

+0

Итак, компиляция модуля ядра прошла успешно (спасибо и извините, что я не могу выдвинуть ваш комментарий ...). Но я до сих пор не могу получать сообщения в Userland. (и точно так же, как и информация: он скомпилировался в последний раз, «ошибка» выше была просто предупреждением и как это не является источником проблемы.) – user4122787

ответ

1

Это не является прямым ответом на NETLINK вопрос, но альтернативное решение. См. Комментарии выше о ограничениях netlink.

UDP-сокеты могут использоваться в Linux для связи между процессом пользовательского режима, таким как демон, и компонентом режима ядра, таким как загружаемый модуль.

Daemon код my_udp.c:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <string.h> 
#include <signal.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <arpa/inet.h> 
#include <string.h> 
#include <errno.h> 
#include <pthread.h> 

static int rcv_sock; 
static int snd_sock; 
static struct sockaddr_in rcv_addr_in; 
static struct sockaddr_in snd_addr_in; 
static pthread_t rcv_thread; 

static void *rcv_thread_fn(void *data); 

int my_udp_init(void) 
{ 
    int sendlen, receivelen; 
    int received = 0; 

    if ((rcv_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 
      perror("socket"); 
      return -1; 
    } 

    if ((snd_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 
      perror("socket"); 
      return -1; 
    } 

    memset(&rcv_addr_in, 0, sizeof(rcv_addr_in)); 
    rcv_addr_in.sin_family = AF_INET; 
    rcv_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    rcv_addr_in.sin_port = htons(MY_IN_PORT); 

    receivelen = sizeof(rcv_addr_in); 
    if (bind(rcv_sock, (struct sockaddr *) &rcv_addr_in, receivelen) < 0) { 
      perror("bind"); 
      return -1; 
    } 

    memset(&snd_addr_in, 0, sizeof(snd_addr_in)); 
    snd_addr_in.sin_family = AF_INET; 
    snd_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    snd_addr_in.sin_port = htons(MY_OUT_PORT); 

    if (pthread_create(&rcv_thread, NULL, rcv_thread_fn, (void *)"rcv_thread")) { 
      return -ENOMEM; 
    } 

    return 0; 
} 

void my_udp_cleanup(void) 
{ 
    pthread_join(rcv_thread, NULL); 
    close(rcv_sock); 
    close(snd_sock); 
} 

int my_snd_msg(const char *buf, int size) 
{ 
    sendto(snd_sock, buf, size, 0, (struct sockaddr *)&snd_addr_in, sizeof(snd_addr_in)); 
    return 0; 
} 

int my_rcv_msg(char *buf, int size) 
{ 
    int cnt = 0; 
    if ((cnt = recv(rcv_sock, buf, size, MSG_DONTWAIT)) < 0) { 
      if (errno == EAGAIN) { 
        /* This is ok in the non-blocking case. */ 
        sleep(1); 
        return 0; 
      } else { 
        perror("recv"); 
        return -1; 
      } 
    } 
    return cnt; 
} 

static void *rcv_thread_fn(void *data) 
{ 
    char buffer[64]; 
    int cnt; 

    while (!g_stop) { 
      cnt = my_rcv_msg(buffer, 63); 
      if (cnt > 0) { 
        printf("message: %s\n", buffer); 
      } 
    } 

    pthread_exit(0); 
} 

Kernel код модуля k_udp.c:

#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/in.h> 
#include <net/sock.h> 
#include <linux/skbuff.h> 
#include <linux/delay.h> 
#include <linux/inet.h> 
#include <linux/kthread.h> 

static struct work_struct rcv_worker; 
static struct socket *in_socket = NULL; 
static struct socket *out_socket = NULL; 
static struct workqueue_struct *wq = NULL; 
static struct task_struct *notify_thread = NULL; 

void rcv_work_queue(struct work_struct *data) 
{ 
     int len; 
     printk(KERN_INFO "%s: *******\n", __func__); 

     while ((len = skb_queue_len(&in_socket->sk->sk_receive_queue)) > 0) { 
       struct sk_buff *skb = NULL; 

       skb = skb_dequeue(&in_socket->sk->sk_receive_queue); 
       printk("message len: %i message: %s\n", skb->len - 8, skb->data+8); 
       kfree_skb(skb); 
     } 
} 

static void cb_data(struct sock *sk, int bytes) 
{ 
     printk(KERN_INFO "%s: *******\n", __func__); 
     queue_work(wq, &rcv_worker); 
} 

void send_notification(char *text) 
{ 
     struct sockaddr_in to_addr; 
     struct msghdr msg; 
     struct iovec iov; 
     mm_segment_t oldfs; 
     int len = 0; 

     if (out_socket->sk == NULL) { 
       printk(KERN_ERR "%s: socket skbuff is null\n", __func__); 
       return; 
     } 

     iov.iov_base = text; 
     len = strlen(text); 
     iov.iov_len = len; 

     memset(&to_addr, 0, sizeof(to_addr)); 
     to_addr.sin_family = AF_INET; 
     to_addr.sin_addr.s_addr = in_aton("127.0.0.1"); 
     to_addr.sin_port = htons(MY_OUT_PORT); 

     msg.msg_flags = 0; 
     msg.msg_name = &to_addr; 
     msg.msg_namelen = sizeof(struct sockaddr_in); 
     msg.msg_control = NULL; 
     msg.msg_controllen = 0; 
     msg.msg_iov = &iov; 
     msg.msg_iovlen = 1; 
     msg.msg_control = NULL; 

     oldfs = get_fs(); 
     set_fs(KERNEL_DS); 
     sock_sendmsg(out_socket, &msg, len); 
     set_fs(oldfs); 
} 

static int k_udp_notify_thread(void *data) 
{ 
     int i = 0; 
     while (!kthread_should_stop()) { 
       char buf[64]; 

       sprintf(buf, "test from kernel%d\n", i++); 
       send_notification(buf); 
       msleep(1000); 
     } 
     return 0; 
} 

int k_udp_init(void) 
{ 
     struct sockaddr_in addr_out; 
     struct sockaddr_in addr_in; 
     int rc = 0; 
     printk("%s\n", __func__); 
     if (in_socket) { 
       printk(KERN_INFO "%s: socket already set up\n", __func__); 
       return 0; 
     } 

     if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &in_socket) < 0) { 
       printk(KERN_ERR "%s: failed to create socket\n", __func__); 
       return -EIO; 
     } 
     addr_in.sin_family = AF_INET; 
     addr_in.sin_addr.s_addr = in_aton("127.0.0.1"); 
     addr_in.sin_port = htons((unsigned short)MY_IN_PORT); 
     rc = in_socket->ops->bind(in_socket, (struct sockaddr *)&addr_in, sizeof(addr_in)); 
     if (rc) { 
       printk(KERN_ERR "%s: failed to bind\n", __func__); 
       sock_release(in_socket); 
       in_socket = NULL; 
       return -EIO; 
     } 
     in_socket->sk->sk_data_ready = cb_data; 

     if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &out_socket) < 0) { 
       printk(KERN_ERR "%s: failed to create socket\n", __func__); 
       sock_release(in_socket); 
       in_socket = NULL; 
       return -EIO; 
     } 
     addr_out.sin_family = AF_INET; 
     addr_out.sin_addr.s_addr = in_aton("127.0.0.1"); 
     addr_out.sin_port = htons((unsigned short)MY_OUT_PORT); 
     rc = out_socket->ops->connect(out_socket, (struct sockaddr *)&addr_out, sizeof(addr_out), 0); 
     if (rc) { 
       printk(KERN_ERR "%s: failed to connect\n", __func__); 
       sock_release(in_socket); 
       in_socket = NULL; 
       sock_release(out_socket); 
       out_socket = NULL; 
       return -EIO; 
     } 

     notify_thread = kthread_create(k_udp_notify_thread, NULL, "k_notify_thread"); 
     if (notify_thread) { 
       printk(KERN_INFO "%s: notify thread created\n", __func__); 
       wake_up_process(notify_thread); 
     } else { 
       printk(KERN_ERR "%s: failed to create notify thread\n", __func__); 
     } 

     INIT_WORK(&rcv_worker, rcv_work_queue); 
     wq = create_singlethread_workqueue("k_rcv_wq"); 
     if (!wq) { 
       return -ENOMEM; 
     } 

     printk(KERN_INFO "%s: success\n", __func__); 

     return 0; 
} 

void k_udp_cleanup(void) 
{ 
     /* Should we check that the thread is still valid (hasn't exited)? */ 
     if (notify_thread) { 
       kthread_stop(notify_thread); 
       notify_thread = NULL; 
     } 

     if (in_socket) { 
       sock_release(in_socket); 
       in_socket = NULL; 
     } 

     if (out_socket) { 
       sock_release(out_socket); 
       out_socket = NULL; 
     } 
     if (wq) { 
       flush_workqueue(wq); 
       destroy_workqueue(wq); 
       wq = NULL; 
     } 
} 

Примечание: Я переименовал некоторые из переменных и имена функций из код, который я использую, поэтому вам может потребоваться внести изменения в компиляцию (если я что-то пропустил). Обязательно совместите порты между компонентами пользователя/ядра.

Вышеприведенный код был получен из нескольких образцов в сети и в ядре Linux.

+0

Спасибо за обмен кодом. Вы скомпилировали его с помощью netlink linux или с libnl? Извините, я даже не могу отложить этот ответ, чтобы поблагодарить вас за усилия ... – user4122787

+0

@ user4122787 Я добавлю заголовки. Демон был скомпилирован с помощью libpthreads. libnl не нужен. –

+0

Пришлось добавить определения для портов ввода и вывода. Модуль ядра компилируется! Еще один вопрос: вы объявили g_stop в пользовательском демоне? (while (! g_stop)) – user4122787

2

Я хотел бы начать, сказав, что я не большой поклонник Netlink; Я думаю, что это довольно плохо спроектировано. Тем не менее, я думаю, что у меня есть точный ответ на вопрос, так что вот оно.

Основная проблема заключается в том, что перед использованием общего семейства Netlink вам сначала необходимо зарегистрировать его (это также относится к обычным семействам Netlink). Ядро не может обрабатывать семьи, которых он не знает. Если вы не используете уже существующее семейство, это формирует то, как вы должны приближаться к Netlink.

A Generic Netlink Family belongs to a kernel module. Это означает, что клиент пользовательского пространства не может создать семью. В свою очередь, это означает, что вы не можете просто запустить клиент, а затем модуль отправит сообщение сразу после его создания. Это происходит потому, что семья не существует в тот момент, когда клиент хотел связать себя с ней.

Что вам нужно сделать, это:

  1. Создать и зарегистрировать семью и группу многоадресной передачи, а вы вставляя модуль.
  2. Запустите клиент пользовательского пространства и привяжите его к семейству и многоадресной группе.
  3. Имейте модуль ядра отправить сообщение в какой-то момент (после связывания клиента).
  4. Клиент пользовательского пространства теперь получает сообщение.
  5. Когда модуль удален, он должен отменить регистрацию семьи.

Ниже приведена моя версия вашего кода. Это модуль ядра. Как вы можете видеть, я решил отправить сообщение несколько раз по таймеру, который запускается каждые две секунды. Это дает вам время, чтобы запустить клиент:

#include <linux/kernel.h> 
    #include <linux/module.h> 
    #include <net/genetlink.h> 

    static struct timer_list timer; 

    /** 
    * This callback runs whenever the socket receives messages. 
    * We don't use it now, but Linux complains if we don't define it. 
    */ 
    static int hello(struct sk_buff *skb, struct genl_info *info) 
    { 
      pr_info("Received a message in kernelspace.\n"); 
      return 0; 
    } 

    /** 
    * Attributes are fields of data your messages will contain. 
    * The designers of Netlink really want you to use these instead of just dumping 
    * data to the packet payload... and I have really mixed feelings about it. 
    */ 
    enum attributes { 
      /* 
      * The first one has to be a throwaway empty attribute; I don't know 
      * why. 
      * If you remove it, ATTR_HELLO (the first one) stops working, because 
      * it then becomes the throwaway. 
      */ 
      ATTR_DUMMY, 
      ATTR_HELLO, 
      ATTR_FOO, 

      /* This must be last! */ 
      __ATTR_MAX, 
    }; 

    /** 
    * Here you can define some constraints for the attributes so Linux will 
    * validate them for you. 
    */ 
    static struct nla_policy policies[] = { 
        [ATTR_HELLO] = { .type = NLA_STRING, }, 
        [ATTR_FOO] = { .type = NLA_U32, }, 
    }; 

    /** 
    * Message type codes. All you need is a hello sorta function, so that's what 
    * I'm defining. 
    */ 
    enum commands { 
      COMMAND_HELLO, 

      /* This must be last! */ 
      __COMMAND_MAX, 
    }; 

    /** 
    * Actual message type definition. 
    */ 
    struct genl_ops ops[] = { 
      { 
        .cmd = COMMAND_HELLO, 
        .flags = 0, 
        .policy = policies, 
        .doit = hello, 
        .dumpit = NULL, 
      }, 
    }; 

    /** 
    * A Generic Netlink family is a group of listeners who can and want to speak 
    * your language. 
    * Anyone who wants to hear your messages needs to register to the same family 
    * as you. 
    */ 
    struct genl_family family = { 
        .id = GENL_ID_GENERATE, 
        .hdrsize = 0, 
        .name = "PotatoFamily", 
        .version = 1, 
        .maxattr = __ATTR_MAX, 
    }; 

    /** 
    * And more specifically, anyone who wants to hear messages you throw at 
    * specific multicast groups need to register themselves to the same multicast 
    * group, too. 
    */ 
    struct genl_multicast_group groups[] = { 
      { .name = "PotatoGroup" }, 
    }; 

    void send_multicast(unsigned long arg) 
    { 
      struct sk_buff *skb; 
      void *msg_head; 
      unsigned char *msg = "TEST"; 
      int error; 

      pr_info("----- Running timer -----\n"); 

      pr_info("Newing message.\n"); 
      skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 
      if (!skb) { 
        pr_err("genlmsg_new() failed.\n"); 
        goto end; 
      } 

      pr_info("Putting message.\n"); 
      msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO); 
      if (!msg_head) { 
        pr_err("genlmsg_put() failed.\n"); 
        kfree_skb(skb); 
        goto end; 
      } 

      pr_info("Nla_putting string.\n"); 
      error = nla_put_string(skb, ATTR_HELLO, msg); 
      if (error) { 
        pr_err("nla_put_string() failed: %d\n", error); 
        kfree_skb(skb); 
        goto end; 
      } 

      pr_info("Nla_putting integer.\n"); 
      error = nla_put_u32(skb, ATTR_FOO, 12345); 
      if (error) { 
        pr_err("nla_put_u32() failed: %d\n", error); 
        kfree_skb(skb); 
        goto end; 
      } 

      pr_info("Ending message.\n"); 
      genlmsg_end(skb, msg_head); 

      pr_info("Multicasting message.\n"); 
      /* 
      * The family has only one group, so the group ID is just the family's 
      * group offset. 
      * mcgrp_offset is supposed to be private, so use this value for debug 
      * purposes only. 
      */ 
      pr_info("The group ID is %u.\n", family.mcgrp_offset); 
      error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL); 
      if (error) { 
        pr_err("genlmsg_multicast_allns() failed: %d\n", error); 
        pr_err("(This can happen if nobody is listening. " 
            "Because it's not that unexpected, " 
            "you might want to just ignore this error.)\n"); 
        goto end; 
      } 

      pr_info("Success.\n"); 
    end: 
      mod_timer(&timer, jiffies + msecs_to_jiffies(2000)); 
    } 

    static int init_socket(void) 
    { 
      int error; 

      pr_info("Registering family.\n"); 
      error = genl_register_family_with_ops_groups(&family, ops, groups); 
      if (error) 
        pr_err("Family registration failed: %d\n", error); 

      return error; 
    } 

    static void initialize_timer(void) 
    { 
      pr_info("Starting timer.\n"); 

      init_timer(&timer); 
      timer.function = send_multicast; 
      timer.expires = 0; 
      timer.data = 0; 

      mod_timer(&timer, jiffies + msecs_to_jiffies(2000)); 
    } 

    static int __init hello_init(void) 
    { 
      int error; 

      error = init_socket(); 
      if (error) 
        return error; 

      initialize_timer(); 

      pr_info("Hello module registered.\n"); 
      return 0; 
    } 

    static void __exit hello_exit(void) 
    { 
      del_timer_sync(&timer); 
      genl_unregister_family(&family); 
      pr_info("Hello removed.\n"); 
    } 

    module_init(hello_init); 
    module_exit(hello_exit); 

    MODULE_LICENSE("GPL"); 

И это клиент в пользовательском пространстве:

#include <netlink/netlink.h> 
    #include <netlink/socket.h> 
    #include <netlink/msg.h> 
    #include <netlink/genl/genl.h> 

    static struct nl_sock *sk = NULL; 

    /** 
    * Attributes and commands have to be the same as in kernelspace, so you might 
    * want to move these enums to a .h and just #include that from both files. 
    */ 
    enum attributes { 
      ATTR_DUMMY, 
      ATTR_HELLO, 
      ATTR_FOO, 

      /* This must be last! */ 
      __ATTR_MAX, 
    }; 

    enum commands { 
      COMMAND_HELLO, 

      /* This must be last! */ 
      __COMMAND_MAX, 
    }; 

    static int fail(int error, char *func_name) 
    { 
      printf("%s() failed.\n", func_name); 
      return error; 
    } 

    static int nl_fail(int error, char *func_name) 
    { 
      printf("%s (%d)\n", nl_geterror(error), error); 
      return fail(error, func_name); 
    } 

    /* 
    * This function will be called for each valid netlink message received 
    * in nl_recvmsgs_default() 
    */ 
    static int cb(struct nl_msg *msg, void *arg) 
    { 
      struct nlmsghdr *nl_hdr; 
      struct genlmsghdr *genl_hdr; 
      struct nlattr *attrs[__ATTR_MAX]; 
      int error; 

      printf("The kernel module sent a message.\n"); 

      nl_hdr = nlmsg_hdr(msg); 
      genl_hdr = genlmsg_hdr(nl_hdr); 

      if (genl_hdr->cmd != COMMAND_HELLO) { 
        printf("Oops? The message type is not Hello; ignoring.\n"); 
        return 0; 
      } 

      error = genlmsg_parse(nl_hdr, 0, attrs, __ATTR_MAX - 1, NULL); 
      if (error) 
        return nl_fail(error, "genlmsg_parse"); 

      /* Remember: attrs[0] is a throwaway. */ 

      if (attrs[1]) 
        printf("ATTR_HELLO: len:%u type:%u data:%s\n", 
            attrs[1]->nla_len, 
            attrs[1]->nla_type, 
            (char *)nla_data(attrs[1])); 
      else 
        printf("ATTR_HELLO: null\n"); 

      if (attrs[2]) 
        printf("ATTR_FOO: len:%u type:%u data:%u\n", 
            attrs[2]->nla_len, 
            attrs[2]->nla_type, 
            *((__u32 *)nla_data(attrs[2]))); 
      else 
        printf("ATTR_FOO: null\n"); 

      return 0; 
    } 

    static int do_things(void) 
    { 
      struct genl_family *family; 
      int group; 
      int error; 

      /* Socket allocation yadda yadda. */ 
      sk = nl_socket_alloc(); 
      if (!sk) 
        return fail(-1, "nl_socket_alloc"); 

      nl_socket_disable_seq_check(sk); 

      error = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL); 
      if (error) 
        return nl_fail(error, "nl_socket_modify_cb"); 

      error = genl_connect(sk); 
      if (error) 
        return nl_fail(error, "genl_connect"); 

      /* Find the multicast group identifier and register ourselves to it. */ 
      group = genl_ctrl_resolve_grp(sk, "PotatoFamily", "PotatoGroup"); 
      if (group < 0) 
        return nl_fail(group, "genl_ctrl_resolve_grp"); 

      printf("The group is %u.\n", group); 

      error = nl_socket_add_memberships(sk, group, 0); 
      if (error) { 
        printf("nl_socket_add_memberships() failed: %d\n", error); 
        return error; 
      } 

      /* Finally, receive the message. */ 
      nl_recvmsgs_default(sk); 

      return 0; 
    } 

    int main(void) 
    { 
      int error; 

      error = do_things(); 

      if (sk) 
        nl_socket_free(sk); 

      return error; 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^