-
Notifications
You must be signed in to change notification settings - Fork 171
/
vr_genetlink.c
180 lines (145 loc) · 4.61 KB
/
vr_genetlink.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* vr_genetlink.c -- generic netlink stuff needed for vnsw
*
* Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <linux/version.h>
#include <net/genetlink.h>
#include "vr_genetlink.h"
#include "vr_types.h"
#include "vr_message.h"
#include "sandesh.h"
static int netlink_trans_request(struct sk_buff *, struct genl_info *);
static struct genl_ops vrouter_genl_ops[] = {
{
.cmd = SANDESH_REQUEST,
.doit = netlink_trans_request,
},
};
struct genl_family vrouter_genl_family = {
.id = GENL_ID_GENERATE,
.name = "vrouter",
.version = 1,
.maxattr = NL_ATTR_MAX,
.netnsok = true,
};
#define NETLINK_RESPONSE_HEADER_LEN (NLMSG_HDRLEN + GENL_HDRLEN + \
NLA_HDRLEN)
#define NETLINK_BUFFER(skb_data) ((char *)skb_data + \
NETLINK_RESPONSE_HEADER_LEN)
#define NETLINK_SKB(buf) *(struct sk_buff **)((char *)buf - \
NETLINK_RESPONSE_HEADER_LEN)
static char *
netlink_trans_alloc(unsigned int size)
{
struct sk_buff *skb;
int len;
len = NETLINK_RESPONSE_HEADER_LEN;
len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
skb = alloc_skb(size + len, GFP_ATOMIC);
if (!skb)
return NULL;
/* Store the skb address at the beginning of the skb itself */
*(struct sk_buff **)(skb->data) = skb;
return skb->data + NETLINK_RESPONSE_HEADER_LEN;
}
static struct sk_buff *
netlink_skb(char *buf)
{
return NETLINK_SKB(buf);
}
static void
netlink_trans_free(char *buf)
{
struct sk_buff *skb;
skb = netlink_skb(buf);
kfree_skb(skb);
return;
}
static int
netlink_trans_request(struct sk_buff *in_skb, struct genl_info *info)
{
char *buf;
unsigned int len;
uint32_t multi_flag;
struct nlmsghdr *rep, *nlh = info->nlhdr;
struct genlmsghdr *genlh;
struct nlattr **aap = info->attrs;
struct nlattr *nla;
struct vr_message request, *response;
struct sk_buff *skb;
uint32_t netlink_id;
if (!aap || !(nla = aap[NL_ATTR_VR_MESSAGE_PROTOCOL]))
return -EINVAL;
request.vr_message_buf = nla_data(nla);
request.vr_message_len = nla_len(nla);
vr_message_request(&request);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
netlink_id = NETLINK_CB(in_skb).pid;
#else
netlink_id = NETLINK_CB(in_skb).portid;
#endif
multi_flag = 0;
while ((response = vr_message_dequeue_response())) {
if ((multi_flag == 0) && (!vr_response_queue_empty()))
multi_flag = NLM_F_MULTI;
buf = response->vr_message_buf;
skb = netlink_skb(buf);
if (!skb)
continue;
len = response->vr_message_len;
len += GENL_HDRLEN + NLA_HDRLEN;
len = NLMSG_ALIGN(len);
rep = __nlmsg_put(skb, netlink_id, nlh->nlmsg_seq,
nlh->nlmsg_type, len, multi_flag);
genlh = nlmsg_data(rep);
memcpy(genlh, info->genlhdr, sizeof(*genlh));
nla = (struct nlattr *)((char *)genlh + GENL_HDRLEN);
nla->nla_len = response->vr_message_len;
nla->nla_type = NL_ATTR_VR_MESSAGE_PROTOCOL;
netlink_unicast(in_skb->sk, skb, netlink_id, MSG_DONTWAIT);
response->vr_message_buf = NULL;
vr_message_free(response);
}
if (multi_flag) {
skb = alloc_skb(NLMSG_HDRLEN, GFP_ATOMIC);
if (!skb)
return 0;
__nlmsg_put(skb, netlink_id, nlh->nlmsg_seq, NLMSG_DONE, 0, 0);
netlink_unicast(in_skb->sk, skb, netlink_id, MSG_DONTWAIT);
}
return 0;
}
static struct vr_mtransport netlink_transport = {
.mtrans_alloc = netlink_trans_alloc,
.mtrans_free = netlink_trans_free,
};
void
vr_genetlink_exit(void)
{
genl_unregister_family(&vrouter_genl_family);
vr_message_transport_unregister(&netlink_transport);
return;
}
int
vr_genetlink_init(void)
{
int ret;
ret = vr_message_transport_register(&netlink_transport);
if (ret)
return ret;
#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) && \
(!(defined(RHEL_MAJOR) && (RHEL_MAJOR >= 7))))
return genl_register_family_with_ops(&vrouter_genl_family, vrouter_genl_ops,
ARRAY_SIZE(vrouter_genl_ops));
#else
return genl_register_family_with_ops(&vrouter_genl_family,
vrouter_genl_ops);
#endif
}