-
Notifications
You must be signed in to change notification settings - Fork 171
/
vr_genetlink.c
216 lines (179 loc) · 5.82 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
* 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"
#include "vr_response.h"
#include "vrouter.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,
.flags = GENL_ADMIN_PERM,
},
};
struct genl_family vrouter_genl_family = {
.id = GENL_ID_GENERATE,
.name = "vrouter",
.version = 1,
.maxattr = NL_ATTR_MAX - 1,
.netnsok = true,
};
struct genl_multicast_group vrouter_genl_groups[] = {
{ .name = "VRouterGroup" },
};
#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;
int ret;
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;
void *msg_head;
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);
ret = vr_message_request(&request);
if (ret < 0) {
if (vr_send_response(ret))
return ret;
}
#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 (!response->vr_message_broadcast) {
if ((multi_flag == 0) && (!vr_response_queue_empty()))
multi_flag = NLM_F_MULTI;
buf = response->vr_message_buf;
skb = netlink_skb(buf);
if (!skb)
goto next;
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);
} else {
// If there is no listener, we don't broadcast
if (!netlink_has_listeners(in_skb->sk, vrouter_genl_family.mcgrp_offset)) {
goto next;
}
skb = genlmsg_new(nla->nla_len, GFP_KERNEL);
if (!skb)
goto next;
msg_head = genlmsg_put(skb, 0, 0, &vrouter_genl_family, 0, SANDESH_REQUEST);
if (!msg_head) {
nlmsg_free(skb);
goto next;
}
if (nla_put(skb, NL_ATTR_VR_MESSAGE_PROTOCOL, response->vr_message_len, response->vr_message_buf) < 0) {
nlmsg_free(skb);
goto next;
}
genlmsg_end(skb, msg_head);
genlmsg_multicast(&vrouter_genl_family, skb, 0, 0, GFP_KERNEL);
}
next:
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_groups(&vrouter_genl_family,
vrouter_genl_ops, vrouter_genl_groups);
#endif
}