如何在 Linux 内核中添加新的自定义第 4 层协议(新的原始套接字)?

Abh*_*gar 5 sockets linux networking raw-sockets linux-kernel

我正在尝试在 linux (ubuntu 14.04) 中添加我自己的自定义第 4 层协议 - IPPROTO_MYPROTO作为可加载内核模块。我已完成注册协议的所有必要步骤。我在这里分享我的代码。

当我尝试使用sendmsg()从用户空间程序发送消息时,我希望应该在内核空间中调用通过struct proto结构注册的相应 fn myproto_sendmsg() 。但我观察到的是,虽然内核空间中的myproto_sendmsg()没有被调用,但目标机器正在接收正确的数据。惊喜 !惊喜 !。默认的 udp sendmsg() fn 在这里启动,就像不速之客在做他的工作一样。

这里,用户空间中的 sendmsg() 调用返回与 send 一样多的字节。因此,fn 返回成功。

用户空间程序:

void forwardUDP( int destination_node ,char sendString[] )
{
    struct msghdr msg;
    destination_node = destination_node % N;                //destination node to which data is to be forwaded
    int sock, rc;
    struct sockaddr_in server_addr;
    struct iovec iov;

    struct hostent *host;                   //hostent predefined structure use to store info about host
    host = (struct hostent *) gethostbyname(node[destination_node].ip_address);//gethostbyname returns a pointer to hostent
    if ((sock = socket(AF_INET, SOCK_RAW, 5)) == -1)
    {

            perror("socket");
            exit(1);
    }

    //destination address structure
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(node[destination_node].udpportno);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);       //host->h_addr gives address of host
    bzero(&(server_addr.sin_zero),8);

    /* fill the messsage structure*/
    memset(&msg, 0 , sizeof(struct msghdr));
    memset(&iov, 0, sizeof(struct iovec));
    msg.msg_name = (void *)&server_addr;
    msg.msg_namelen = sizeof(struct sockaddr_in);
    printf("sendString = %s\n", sendString);
    iov.iov_base = (void *)sendString;
    iov.iov_len = strlen(sendString);
    msg.msg_iov = &iov;
    printf("len = %d\n", strlen(sendString));
    #if 1
    msg.msg_iovlen = 1;
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_flags = 0;
    #endif
    //sendto(sock, sendString, strlen(sendString), 0,(struct sockaddr *)&server_addr, sizeof(struct sockaddr));

    **rc = sendmsg(sock, &msg, 0);**
    printf("rc = %d\n", rc);
    //sendto() function shall send a message through a connectionless-mode socket.
    printf("\nFORWARD REQUEST : '%s' has been forwarded to node ---->%d\n",sendString,destination_node);
    //close(sock);
}
Run Code Online (Sandbox Code Playgroud)

内核模块

/* Define the handler which will recieve all ingress packets for protocol = IPPROTO_MYPROTO
   defined in net/protocol.h
*/

/* Resgiter the call backs for pkt reception */
static const struct net_protocol myproto_protocol = {
    .handler = myproto_rcv,
    .err_handler = myproto_err,
    .no_policy = 1,
    .netns_ok = 1,
};

static struct inet_protosw      myproto_protosw;
int
myproto_rcv(struct sk_buff *skb){
    printk(KERN_INFO "myproto_rcv is called\n");
    return 0;
}

int
myproto_sendmsg(struct kiocb *iocb, struct sock *sk,
            struct msghdr *msg, size_t len){
    printk(KERN_INFO "myproto_sendmsg() is called\n");
    return 0;
}

void myproto_lib_close(struct sock *sk, long timeout){
    printk(KERN_INFO "close is called\n");
    return;
}

int
myproto_recvmsg(struct kiocb *iocb, struct sock *sk,
            struct msghdr *msg,
            size_t len, int noblock, int flags,
            int *addr_len){
    printk(KERN_INFO "myproto_recvmsg() is called.\n");
    printk(KERN_INFO "iocb = 0x%x,\nsk = 0x%x,\nmsg = 0x%x,\nlen = %d,\nnoblock = %d,\nflags = %d,\naddr_len = 0x%x", iocb, sk, msg, len, noblock, flags, addr_len);
    return 0;
}

/* Socket structure for Custom protocol, see struct udp_sock for example*/

struct myproto_sock{
     struct inet_sock inet; // should be first member
     __u16            len;
};

void
myproto_lib_hash(struct sock *sk){
    printk(KERN_INFO "myproto_lib_hash() is called");
}

/* Define the **struct proto** structure for the Custom protocol defined in
   net/sock.h */
struct proto myproto_prot = {
        .name              = "MYPROTO",
        .owner             = THIS_MODULE,
        .close             = myproto_lib_close,
        .sendmsg           = myproto_sendmsg,
        .hash              = myproto_lib_hash,
        .recvmsg           = myproto_recvmsg,
        .obj_size          = sizeof(struct myproto_sock),
        .slab_flags        = SLAB_DESTROY_BY_RCU,
};

int init_module(void);
void cleanup_module(void);

int
init_module(void)
{
    int rc = 0;
    rc = proto_register(&myproto_prot, 1);

    if(rc == 0){
            printk(KERN_INFO "Protocol registration is successful\n");
    }
    else{
            printk(KERN_INFO "Protocol registration is failed\n");
            cleanup_module();
            return rc;
    }
    rc = inet_add_protocol(&myproto_protocol, IPPROTO_MYPROTO);
    if(rc == 0){
            printk(KERN_INFO "Protocol insertion in inet_protos[] is successful\n");
    }
    else{
            printk(KERN_INFO "Protocol insertion in inet_protos[] is failed\n");
            cleanup_module();
            return rc;
    }

    memset(&myproto_protosw, 0 ,sizeof(myproto_protosw));
    myproto_protosw.type = SOCK_RAW;
    myproto_protosw.protocol = IPPROTO_MYPROTO;
    myproto_protosw.prot = &myproto_prot;

    extern const struct proto_ops inet_dgram_ops; // defined in ipv4/af_inet.c

    myproto_protosw.ops = &inet_dgram_ops;
    myproto_protosw.flags = INET_PROTOSW_REUSE;
    inet_register_protosw(&myproto_protosw);

    return 0;
}


void cleanup_module(void)
{
    int rc = 0;
    rc = inet_del_protocol(&myproto_protocol, IPPROTO_MYPROTO);
    if(rc == 0)
            printk(KERN_INFO "Protocol removed successful\n");
    else
            printk(KERN_INFO "Protocol removal failed\n");

    proto_unregister(&myproto_prot);
    printk(KERN_INFO "Module cleaned up\n");
    return;
}
Run Code Online (Sandbox Code Playgroud)