Initial import
[openvswitch] / datapath / linux-2.4 / compat-2.4 / netlink.c
1 /*
2  * NETLINK      Netlink attributes
3  *
4  *              Authors:        Thomas Graf <tgraf@suug.ch>
5  *                              Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
6  */
7
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/errno.h>
11 #include <linux/jiffies.h>
12 #include <linux/netdevice.h>
13 #include <linux/skbuff.h>
14 #include <linux/string.h>
15 #include <linux/types.h>
16 #include <net/netlink.h>
17 #include <net/sock.h>
18
19 /**
20  * netlink_queue_skip - Skip netlink message while processing queue.
21  * @nlh: Netlink message to be skipped
22  * @skb: Socket buffer containing the netlink messages.
23  *
24  * Pulls the given netlink message off the socket buffer so the next
25  * call to netlink_queue_run() will not reconsider the message.
26  */
27 static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb)
28 {
29         int msglen = NLMSG_ALIGN(nlh->nlmsg_len);
30
31         if (msglen > skb->len)
32                 msglen = skb->len;
33
34         skb_pull(skb, msglen);
35 }
36
37 static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
38                                                      struct nlmsghdr *))
39 {
40         struct nlmsghdr *nlh;
41         int err;
42
43         while (skb->len >= nlmsg_total_size(0)) {
44                 nlh = nlmsg_hdr(skb);
45                 err = 0;
46
47                 if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
48                         return 0;
49
50                 /* Only requests are handled by the kernel */
51                 if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
52                         goto skip;
53
54                 /* Skip control messages */
55                 if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
56                         goto skip;
57
58                 err = cb(skb, nlh);
59                 if (err == -EINTR) {
60                         /* Not an error, but we interrupt processing */
61                         netlink_queue_skip(nlh, skb);
62                         return err;
63                 }
64 skip:
65                 if (nlh->nlmsg_flags & NLM_F_ACK || err)
66                         netlink_ack(skb, nlh, err);
67
68                 netlink_queue_skip(nlh, skb);
69         }
70
71         return 0;
72 }
73
74 /**
75  * netlink_run_queue - Process netlink receive queue.
76  * @sk: Netlink socket containing the queue
77  * @qlen: Place to store queue length upon entry
78  * @cb: Callback function invoked for each netlink message found
79  *
80  * Processes as much as there was in the queue upon entry and invokes
81  * a callback function for each netlink message found. The callback
82  * function may refuse a message by returning a negative error code
83  * but setting the error pointer to 0 in which case this function
84  * returns with a qlen != 0.
85  *
86  * qlen must be initialized to 0 before the initial entry, afterwards
87  * the function may be called repeatedly until qlen reaches 0.
88  *
89  * The callback function may return -EINTR to signal that processing
90  * of netlink messages shall be interrupted. In this case the message
91  * currently being processed will NOT be requeued onto the receive
92  * queue.
93  */
94 void netlink_run_queue(struct sock *sk, unsigned int *qlen,
95                        int (*cb)(struct sk_buff *, struct nlmsghdr *))
96 {
97         struct sk_buff *skb;
98
99         if (!*qlen || *qlen > skb_queue_len(&sk->receive_queue))
100                 *qlen = skb_queue_len(&sk->receive_queue);
101
102         for (; *qlen; (*qlen)--) {
103                 skb = skb_dequeue(&sk->receive_queue);
104                 if (netlink_rcv_skb(skb, cb)) {
105                         if (skb->len)
106                                 skb_queue_head(&sk->receive_queue, skb);
107                         else {
108                                 kfree_skb(skb);
109                                 (*qlen)--;
110                         }
111                         break;
112                 }
113
114                 kfree_skb(skb);
115         }
116 }