• Kernel与用户进程通信


      测试IPv6 ready logo   rfc 3315的时候,遇到一个问题,要求在收到ICMPv6 RA的时候,DHCPv6  Client要发Solicit消息。在平常的应用中,都是启动DHCPv6  Client后,client每隔一段时间就会发送Solicit,而并不是要在收到RA的时候发送。于是就修改了下kernel和client的代码,使得在kernel收到RA消息时,发送消息给DHCPv6  Client,使client发送Solicit。

          思路为:使用netlink_kernel_create建立消息机制。在DHCPv6 Client初始化的时候,新建类型为NETLINK_RTK_ICMP6_RA  的netlink socket,监听kernel的消息,并且把自己的pid发送给kernel, kernel 收到消息后,存储pid为全局变量。当kernel收到RA包后,将发送消息给pid的用户进程。用户进程收到消息,发送solicit。

                    Kernel中接收ICMPv6 RA的代码在linux-3.10/net/ipv6/ndisc.c。

    Kernel:

    1. 初始化IPC

    在ndisc_init()中加上进程通信的socket。

                    //create netlink socket

                    struct netlink_kernel_cfg cfg = {

                                    .input    = nl_icmp_input,

                    };

                    printk("%s: before netlink_kernel_create ", __func__);

                    icmp6_ra_sock = netlink_kernel_create(&init_net, NETLINK_RTK_ICMP6_RA, &cfg);

                    if (icmp6_ra_sock == NULL) {

                                    printk(KERN_ERR "Netlink[Kernel] Cannot create netlink socket for ipv6 ra. ");

                                    return -EAFNOSUPPORT;

                    }

                    printk("Netlink[Kernel] create netlink socket for ipv6 ra ok. ");

                                    其中NETLINK_RTK_ICMP6_RA  为在netlink.h中定义的一个类型。nl_icmp_input用于接收用户层发来的信息。

    2. 接收DHCPv6 Client发来的pid 信息

    static void nl_icmp_input(struct sk_buff  *__skb)

    {

                    struct sk_buff *skb;

                    struct nlmsghdr *nlh;

                    char str[100];

                    struct completion cmpl;

                    int i=10;

                   

                    skb = skb_get (__skb);

                    if(skb->len >= NLMSG_SPACE(0))

                    {

                                    nlh = nlmsg_hdr(skb);

                                    memcpy(str, NLMSG_DATA(nlh), sizeof(str));

                                    printk("Message received:%s ",str) ;

                                    DHCP6C_PID = nlh->nlmsg_pid;

                                    printk("DHCP PID = %d:%s ", DHCP6C_PID) ;

                                    kfree_skb(skb);

                    }

                    return;

    }

    3. 收到RA后,发消息给用户进程

    在ndisc_router_discovery中,确认RA包ok后,发送消息。

                    if (icmp6_ra_sock) {

                                    printk("%s: ra socket is not null ", __func__);

                                    payloadLen = NLMSG_SPACE(payloadLen);

                                    /*Alloc skb ,this check helps to call the fucntion from interrupt context */

                                    if(in_atomic())

                                    {

                                                    skb_ra = alloc_skb(payloadLen, GFP_ATOMIC);

                                    }

                                    else

                                    {

                                                    skb_ra = alloc_skb(payloadLen, GFP_KERNEL);

                                    }

                                    if(!skb_ra)

                                    {

                                                    printk(KERN_ERR "failed to alloc skb in %s",__FUNCTION__);

                                                    return;

                                    }

                                    nl_msgHdr = (struct nlmsghdr *)skb_ra->data;

                                    nl_msgHdr->nlmsg_type = MESSAGE_ICMP6_RA;

                                    nl_msgHdr->nlmsg_pid=0;/*from kernel */

                                    nl_msgHdr->nlmsg_len = payloadLen;

                                    nl_msgHdr->nlmsg_flags =0;                     

                                    NETLINK_CB(skb_ra).portid = 0; /*from kernel */

                                    skb_ra->len = payloadLen;

                                    err = netlink_unicast(icmp6_ra_sock, skb_ra, DHCP6C_PID, MSG_DONTWAIT); //send to dhcp6c

                                    if (err < 0)

                                                    printk("%s: nfnetlink_unicast icmpv6 ra socket fail ", __func__);

                                    else

                                                    printk("%s: nfnetlink_unicast icmpv6 ra socket ok ", __func__);

                    }

    用户层:

    1. Dhcp6c 初始化,并发送pid给kernel

    int init_ra_monitor_fd()

    {

            int ret = 0;

            struct sockaddr_nl src_addr, dest_addr;

            struct nlmsghdr *nlh = NULL;

            struct iovec iov;

            struct msghdr msg;        

            int state_smg = 0;           

            int pid = getpid();

           

            if ((rasock = socket(AF_NETLINK, SOCK_RAW, NETLINK_RTK_ICMP6_RA)) < 0)

            {

                            printf("Could not open netlink socket for kernel monitor ");

                            return 1;

            }

            else

            {

                            printf("kernelMonitorFd=%d ", rasock);

            }

             memset(&src_addr, 0, sizeof(src_addr));

             src_addr.nl_family = AF_NETLINK;

             src_addr.nl_pid = pid;

             src_addr.nl_groups = 0; // multicast

             if (bind(rasock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0)

             {

                             printf("Could not bind netlink socket for kernel monitor ");

                             close(rasock);

                             rasock = -1;

                             return 1;

             }

            /* Tell kernel dhcp6c pid, kernel will send messages to dhcp6c when it receive ICMPv6 RA */

            /* Fix IPv6 Ready logo rfc3315 */

            memset(&dest_addr, 0, sizeof(dest_addr));

            dest_addr.nl_family = AF_NETLINK;

            dest_addr.nl_pid = 0;

            dest_addr.nl_groups = 0;

           

            nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));

            if(!nlh){

                            printf("malloc nlmsghdr error! ");

                            close(rasock);

                            return -1;

            }

            nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);

            nlh->nlmsg_pid = pid;

            nlh->nlmsg_flags = 0;

            sprintf(NLMSG_DATA(nlh), "%d", pid);

           

            iov.iov_base = (void *)nlh;

            iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);

            memset(&msg, 0, sizeof(msg));

            msg.msg_name = (void *)&dest_addr;

            msg.msg_namelen = sizeof(dest_addr);

            msg.msg_iov = &iov;

            msg.msg_iovlen = 1;

            printf("state_smg ");

            state_smg = sendmsg(rasock, &msg, 0);

            if(state_smg == -1)

            {

                            printf("get error sendmsg = %s ",strerror(errno));

            }             

           

    }

    2. 等待接收消息

    // add to read sockets

    if (rasock >= 0) {

            FD_SET(rasock, &r);

            maxsock = (rasock > maxsock) ? rasock : maxsock;

    }

    //wait

    ret = select(maxsock + 1, &r, NULL, NULL, w);

    if (FD_ISSET(rasock, &r)) {

            printf("%s: have receive the ra message ", __func__);

            dhcp6_ra_recv(); //send solicit

    }

  • 相关阅读:
    Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言
    Scalaz(17)- Monad:泛函状态类型-State Monad
    Scalaz(16)- Monad:依赖注入-Dependency Injection By Reader Monad
    spring mvc注解和spring boot注解
    mac查看网页时翻页
    springboot工程的结构
    关于springboot
    mac锁屏
    关于mac上的maven
    debian dhcp配置
  • 原文地址:https://www.cnblogs.com/chenxuelian/p/4126913.html
Copyright © 2020-2023  润新知