Line data Source code
1 : /*
2 : * vr_info.c -- vr_info functions
3 : *
4 : * Copyright (c) 2020 Juniper Networks, Inc. All rights reserved.
5 : * Copyright Contributors to the OpenSDN project
6 : */
7 :
8 : #include <vr_os.h>
9 : #if defined(__linux__)
10 : #include <linux/version.h>
11 : #endif
12 : #include "vr_types.h"
13 : #include "vr_sandesh.h"
14 : #include "vr_message.h"
15 : #include "vr_cpuid.h"
16 : #include <vr_packet.h>
17 :
18 : /* vr_info_buff_table_p pointer used to handle multiple clients in parallel.
19 : * When user callback function returns outbuf pointer, its stored in this
20 : * table and sends the msg buffer to its corresponding CLI client */
21 : static struct vr_info_buff_table vr_info_buff_table_p[VR_INFO_MSG_BUF_TABLE];
22 :
23 : /* Register the user callback function in below table */
24 : struct vr_info_callback users_cb_reg[VR_INFO_MAX_CALLBACK];
25 :
26 : /* Register callback using below function for vr_info.
27 : * */
28 : static int
29 1 : vr_info_callback_register(void)
30 : {
31 : /* Below macro would be expanded and register each callback function in
32 : * users_cb_reg table */
33 1 : FOREACH_VR_INFO_CB_REG_INIT();
34 :
35 1 : return 0;
36 : }
37 :
38 : /* Sandesh handler for vr_info message */
39 : void
40 1 : vr_info_req_process(void *s_req)
41 : {
42 1 : int i, ret = 0, buff_sz = 0, max_buff_sz = 0;
43 : vr_info_t msg_req;
44 1 : vr_info_req *req = (vr_info_req *)s_req;
45 : vr_info_req resp;
46 1 : struct vr_message_dumper *dumper = NULL;
47 1 : bool vr_info_last_buf = 0;
48 1 : char *trunc = "\n Message Truncated\n";
49 :
50 1 : if (req->h_op != SANDESH_OP_DUMP) {
51 0 : goto generate_response;
52 : }
53 :
54 : /* Copy request to response */
55 1 : resp = *req;
56 :
57 : /* Initialize the dumper buffer */
58 1 : dumper = vr_message_dump_init(req);
59 :
60 : /* Register callabck function before processing client request */
61 1 : ret = vr_info_callback_register();
62 1 : if(ret < 0) {
63 0 : vr_printf("vr_info: Callback registration failed\n with %d\n", ret);
64 0 : goto generate_response;
65 : }
66 :
67 : /* When CLI request comes, call the registered callback function and store
68 : * the address in msg_buff_table, If msginfo is more than 4K, split the
69 : * data into 4k chunks and send it serially to client. The client will
70 : * request back with vdu_buff_table_id and its index.
71 : * */
72 1 : if(!req->vdu_buff_table_id) {
73 : /* Maximum supported Client by server is 64, so based on that message
74 : * buffer table size is determined.
75 : * Buf table_id[0] is unused, so starting i with '1' */
76 1 : for(i = 1; i < VR_INFO_MSG_BUF_TABLE; i++) {
77 : /* Iterate through message buffer table and find the free entry */
78 1 : if(vr_info_buff_table_p[i].buff == NULL) {
79 :
80 1 : memset(&msg_req, 0, sizeof(vr_info_t));
81 :
82 : /* Copy Inbuf request to msg_req */
83 1 : msg_req.inbuf = req->vdu_inbuf;
84 1 : msg_req.inbuf_len = req->vdu_inbuf_size;
85 :
86 : /* Check requested msginfo is within boundary range */
87 1 : if((req->vdu_msginfo <= 0) || (req->vdu_msginfo >= INFO_MAX)) {
88 0 : vr_printf("vr_info: Callback function is not registered \
89 0 : for msginfo %d", req->vdu_msginfo);
90 0 : goto generate_response;
91 : }
92 :
93 : /* If --bufsz is sent from CLI, then copy the value as part of
94 : * msg_req */
95 1 : if(req->vdu_outbufsz) {
96 0 : msg_req.bufsz = req->vdu_outbufsz;
97 : }
98 :
99 : /* Check requested msginfo is avaialable in users callback
100 : * registered table */
101 1 : if(req->vdu_msginfo ==
102 1 : users_cb_reg[req->vdu_msginfo].msginfo) {
103 1 : ret = users_cb_reg[req->vdu_msginfo].cb_fn(&msg_req);
104 : } else {
105 0 : vr_printf("vr_info: Requested msginfo %d is not registered \
106 0 : in VR_INFO_REG() table\n", req->vdu_msginfo);
107 0 : goto generate_response;
108 : }
109 :
110 : /* Callback function returned failure msg */
111 1 : if(ret < 0) {
112 0 : vr_printf("vr_info: User callback function returned \
113 0 : failure for msg %d", req->vdu_msginfo);
114 : /* Suppose if message buffer is not completed, we append
115 : * "Message Truncated" in the buffer and send to client */
116 0 : if(ret == VR_INFO_MSG_TRUNC) {
117 0 : snprintf(((msg_req.outbuf + msg_req.outbuf_len) - strlen(trunc)),
118 : VR_MESSAGE_PAGE_SIZE, "%s", trunc);
119 : } else {
120 0 : goto generate_response;
121 : }
122 : }
123 : /* Copy buffer table id as part of response, so when
124 : * dump_pending is true, will callback with same buf table id */
125 1 : resp.vdu_buff_table_id = i;
126 :
127 : /* Copy message buffer_ptr to buffer table */
128 1 : vr_info_buff_table_p[i].buff = msg_req.outbuf;
129 : /* Copy message buffer length to buff_table pointer */
130 1 : vr_info_buff_table_p[i].buf_len = msg_req.outbuf_len;
131 :
132 : /* Check if Output buffer has contents */
133 1 : if(msg_req.outbuf == NULL) {
134 0 : vr_printf("vrinfo: Output buffer is NULL \n");
135 0 : vr_info_last_buf = true;
136 0 : goto generate_response;
137 :
138 : }
139 : /* If outbuf_len is not supplied from callback, calculate it */
140 1 : if(!msg_req.outbuf_len) {
141 0 : buff_sz = strlen(msg_req.outbuf);
142 : } else {
143 1 : buff_sz = msg_req.outbuf_len;
144 : }
145 :
146 1 : if(!msg_req.outbuf_len) {
147 0 : vr_printf("vrinfo: Output buffer is not filled\n");
148 0 : vr_info_last_buf = true;
149 0 : goto generate_response;
150 : }
151 :
152 : /* this is first iteration, so make vdu_marker as zero */
153 1 : req->vdu_marker = 0;
154 1 : break;
155 : }
156 : }
157 : } else {
158 0 : if(vr_info_buff_table_p[resp.vdu_buff_table_id].buff != NULL) {
159 0 : buff_sz = vr_info_buff_table_p[resp.vdu_buff_table_id].buf_len;
160 : } else {
161 0 : vr_printf("vr_info: vr_info_buff_table_p buf_id %d is NULL\n",
162 0 : req->vdu_buff_table_id);
163 0 : vr_info_last_buf = true;
164 0 : goto generate_response;
165 : }
166 : }
167 :
168 : /* Sandesh has limitation of macro(VR_MESSAGE_PAGE_SIZE) size, so
169 : * calculating max buffer size, and those much amount of data would be sent
170 : * on each iteration */
171 1 : max_buff_sz = VR_MESSAGE_PAGE_SIZE - (sizeof(resp) +
172 : sizeof(struct vr_message_dumper));
173 :
174 : /* Check if the buffer size is greater than max buffer, If so, have to send
175 : * it through multiple iteration. */
176 1 : if(buff_sz > max_buff_sz) {
177 : /* To send whole buffer, (buff_sz/max_buff_sz + 1) => these many
178 : * number of times have to send it to client CLI. */
179 : while(1) {
180 : /* To find the last iteration */
181 0 : if( req->vdu_marker == buff_sz/max_buff_sz ) {
182 : /* Calculate the number of remaining bytes to send it to
183 : * client CLI */
184 0 : resp.vdu_proc_info_size = (buff_sz -
185 0 : (max_buff_sz * req->vdu_marker) + 1);
186 0 : vr_info_last_buf = true;
187 : } else {
188 : /* Increment with 1 to include the '\0' character */
189 0 : resp.vdu_proc_info_size = max_buff_sz + 1;
190 : }
191 :
192 : /* Update the index value, querying for next iteration */
193 0 : resp.vdu_index = (req->vdu_marker + 1);
194 :
195 0 : resp.vdu_proc_info = vr_zalloc((resp.vdu_proc_info_size *
196 : sizeof(uint8_t)), VR_INFO_REQ_OBJECT);
197 0 : if(resp.vdu_proc_info == NULL) {
198 0 : vr_info_last_buf = true;
199 0 : goto generate_response;
200 : }
201 :
202 : /* Copy the message to "resp.vdu_proc_info" from
203 : * "vr_info_buff_table_p[id]+offset". Here offset is calculated
204 : * from (max_buff_sz * req->vdu_marker) */
205 0 : snprintf(resp.vdu_proc_info, (resp.vdu_proc_info_size),
206 0 : "%s", (vr_info_buff_table_p[resp.vdu_buff_table_id].buff +
207 0 : (max_buff_sz * req->vdu_marker)));
208 :
209 : /* If message buffer copy is not complete, it will return with -1.
210 : * so it will send to client and client will request back. */
211 0 : ret = vr_message_dump_object(dumper, VR_INFO_OBJECT_ID, &resp);
212 :
213 0 : if(resp.vdu_proc_info != NULL) {
214 0 : vr_free(resp.vdu_proc_info, VR_INFO_REQ_OBJECT);
215 : }
216 0 : if((ret <= 0) || vr_info_last_buf) {
217 : break;
218 : }
219 : }
220 : } else {
221 : /* Total message buffer size is less than 4k, so it can send in
222 : * one iteration. */
223 1 : vr_info_last_buf = true;
224 1 : resp.vdu_proc_info_size = buff_sz;
225 :
226 1 : resp.vdu_proc_info = vr_zalloc((resp.vdu_proc_info_size *
227 : sizeof(uint8_t)), VR_INFO_REQ_OBJECT);
228 1 : if(resp.vdu_proc_info == NULL) {
229 0 : goto generate_response;
230 : }
231 :
232 : /* Copy message buffer */
233 1 : snprintf(resp.vdu_proc_info, resp.vdu_proc_info_size, "%s", msg_req.outbuf);
234 1 : vr_message_dump_object(dumper, VR_INFO_OBJECT_ID, &resp);
235 :
236 1 : if(resp.vdu_proc_info) {
237 1 : vr_free(resp.vdu_proc_info, VR_INFO_REQ_OBJECT);
238 : }
239 : }
240 :
241 0 : generate_response:
242 1 : vr_message_dump_exit(dumper, ret);
243 :
244 : /* Once last buffer element has reached, clear all message buffers.
245 : * Memory(malloc) is allocated by end users and infra will free the memory */
246 1 : if(vr_info_last_buf) {
247 2 : for (i = resp.vdu_buff_table_id; i > 0; i--) {
248 1 : if(vr_info_buff_table_p[i].buff != NULL) {
249 1 : vr_free(vr_info_buff_table_p[i].buff, VR_INFO_REQ_OBJECT);
250 1 : vr_info_buff_table_p[i].buff = NULL;
251 1 : vr_info_buff_table_p[i].buf_len = 0;
252 : } else
253 0 : vr_printf("Memory free failed for vr_info pointer instance %d\n", i);
254 : }
255 : }
256 :
257 1 : return;
258 : }
|