1 /** @file
2
3 Network message marshalling.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "tscore/ink_config.h"
25 #include "tscore/ink_defs.h"
26 #include "tscore/ink_error.h"
27 #include "tscore/ink_assert.h"
28 #include "tscore/ink_memory.h"
29 #include "mgmtapi.h"
30 #include "NetworkMessage.h"
31
32 #define MAX_OPERATION_FIELDS 16
33
34 struct NetCmdOperation {
35 unsigned nfields;
36 const MgmtMarshallType fields[MAX_OPERATION_FIELDS];
37 };
38
39 // Requests always begin with a OpType, followed by additional fields.
40 static const struct NetCmdOperation requests[] = {
41 /* RECORD_SET */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING}},
42 /* RECORD_GET */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
43 /* PROXY_STATE_GET */ {1, {MGMT_MARSHALL_INT}},
44 /* PROXY_STATE_SET */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
45 /* RECONFIGURE */ {1, {MGMT_MARSHALL_INT}},
46 /* RESTART */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
47 /* BOUNCE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
48 /* STOP */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
49 /* DRAIN */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
50 /* EVENT_RESOLVE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
51 /* EVENT_GET_MLT */ {1, {MGMT_MARSHALL_INT}},
52 /* EVENT_ACTIVE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
53 /* EVENT_REG_CALLBACK */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
54 /* EVENT_UNREG_CALLBACK */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
55 /* EVENT_NOTIFY */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING}}, // only msg sent from TM to
56 // client
57 /* STATS_RESET_NODE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
58 /* STORAGE_DEVICE_CMD_OFFLINE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
59 /* RECORD_MATCH_GET */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
60 /* API_PING */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
61 /* SERVER_BACKTRACE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
62 /* RECORD_DESCRIBE_CONFIG */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
63 /* LIFECYCLE_MESSAGE */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}},
64 /* HOST_STATUS_HOST_UP */ {4, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
65 /* HOST_STATUS_HOST_DOWN */ {4, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
66 };
67
68 // Responses always begin with a TSMgmtError code, followed by additional fields.
69 static const struct NetCmdOperation responses[] = {
70 /* RECORD_SET */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
71 /* RECORD_GET */
72 {5, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}},
73 /* PROXY_STATE_GET */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
74 /* PROXY_STATE_SET */ {1, {MGMT_MARSHALL_INT}},
75 /* RECONFIGURE */ {1, {MGMT_MARSHALL_INT}},
76 /* RESTART */ {1, {MGMT_MARSHALL_INT}},
77 /* BOUNCE */ {1, {MGMT_MARSHALL_INT}},
78 /* STOP */ {1, {MGMT_MARSHALL_INT}},
79 /* DRAIN */ {1, {MGMT_MARSHALL_INT}},
80 /* EVENT_RESOLVE */ {1, {MGMT_MARSHALL_INT}},
81 /* EVENT_GET_MLT */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
82 /* EVENT_ACTIVE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
83 /* EVENT_REG_CALLBACK */ {0, {}}, // no reply
84 /* EVENT_UNREG_CALLBACK */ {0, {}}, // no reply
85 /* EVENT_NOTIFY */ {0, {}}, // no reply
86 /* STATS_RESET_NODE */ {1, {MGMT_MARSHALL_INT}},
87 /* STORAGE_DEVICE_CMD_OFFLINE */ {1, {MGMT_MARSHALL_INT}},
88 /* RECORD_MATCH_GET */
89 {5, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}},
90 /* API_PING */ {0, {}}, // no reply
91 /* SERVER_BACKTRACE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
92 /* RECORD_DESCRIBE_CONFIG */
93 {15,
94 {MGMT_MARSHALL_INT /* status */, MGMT_MARSHALL_STRING /* name */, MGMT_MARSHALL_DATA /* value */,
95 MGMT_MARSHALL_DATA /* default */, MGMT_MARSHALL_INT /* type */, MGMT_MARSHALL_INT /* class */, MGMT_MARSHALL_INT /* version */,
96 MGMT_MARSHALL_INT /* rsb */, MGMT_MARSHALL_INT /* order */, MGMT_MARSHALL_INT /* access */, MGMT_MARSHALL_INT /* update */,
97 MGMT_MARSHALL_INT /* updatetype */, MGMT_MARSHALL_INT /* checktype */, MGMT_MARSHALL_INT /* source */,
98 MGMT_MARSHALL_STRING /* checkexpr */}},
99 /* LIFECYCLE_MESSAGE */ {1, {MGMT_MARSHALL_INT}},
100 /* HOST_STATUS_UP */ {1, {MGMT_MARSHALL_INT}},
101 /* HOST_STATUS_DOWN */ {1, {MGMT_MARSHALL_INT}},
102 };
103
104 #define GETCMD(ops, optype, cmd) \
105 do { \
106 if (static_cast<unsigned>(optype) >= countof(ops)) { \
107 return TS_ERR_PARAMS; \
108 } \
109 if (ops[static_cast<unsigned>(optype)].nfields == 0) { \
110 return TS_ERR_PARAMS; \
111 } \
112 cmd = &ops[static_cast<unsigned>(optype)]; \
113 } while (0);
114
115 TSMgmtError
send_mgmt_request(const mgmt_message_sender & snd,OpType optype,...)116 send_mgmt_request(const mgmt_message_sender &snd, OpType optype, ...)
117 {
118 va_list ap;
119 ats_scoped_mem<char> msgbuf;
120 MgmtMarshallInt msglen;
121 const MgmtMarshallType lenfield[] = {MGMT_MARSHALL_INT};
122 const NetCmdOperation *cmd;
123
124 if (!snd.is_connected()) {
125 return TS_ERR_NET_ESTABLISH; // no connection.
126 }
127
128 GETCMD(requests, optype, cmd);
129
130 va_start(ap, optype);
131 msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
132 va_end(ap);
133
134 msgbuf = static_cast<char *>(ats_malloc(msglen + 4));
135
136 // First marshall the total message length.
137 mgmt_message_marshall((char *)msgbuf, msglen, lenfield, countof(lenfield), &msglen);
138
139 // Now marshall the message itself.
140 va_start(ap, optype);
141 if (mgmt_message_marshall_v((char *)msgbuf + 4, msglen, cmd->fields, cmd->nfields, ap) == -1) {
142 va_end(ap);
143 return TS_ERR_PARAMS;
144 }
145
146 va_end(ap);
147 return snd.send(msgbuf, msglen + 4);
148 }
149
150 TSMgmtError
send_mgmt_request(int fd,OpType optype,...)151 send_mgmt_request(int fd, OpType optype, ...)
152 {
153 va_list ap;
154 MgmtMarshallInt msglen;
155 MgmtMarshallData req = {nullptr, 0};
156 const MgmtMarshallType fields[] = {MGMT_MARSHALL_DATA};
157 const NetCmdOperation *cmd;
158
159 GETCMD(requests, optype, cmd);
160
161 // Figure out the payload length.
162 va_start(ap, optype);
163 msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
164 va_end(ap);
165
166 ink_assert(msglen >= 0);
167
168 req.ptr = static_cast<char *>(ats_malloc(msglen));
169 req.len = msglen;
170
171 // Marshall the message itself.
172 va_start(ap, optype);
173 if (mgmt_message_marshall_v(req.ptr, req.len, cmd->fields, cmd->nfields, ap) == -1) {
174 ats_free(req.ptr);
175 va_end(ap);
176 return TS_ERR_PARAMS;
177 }
178
179 va_end(ap);
180
181 MgmtMarshallInt op;
182 MgmtMarshallString name;
183 int down_time;
184 static const MgmtMarshallType fieldso[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT};
185
186 if (mgmt_message_parse(static_cast<void *>(req.ptr), msglen, fieldso, countof(fieldso), &op, &name, &down_time) == -1) {
187 printf("Plugin message - RPC parsing error - message discarded.\n");
188 }
189
190 // Send the response as the payload of a data object.
191 if (mgmt_message_write(fd, fields, countof(fields), &req) == -1) {
192 ats_free(req.ptr);
193 return TS_ERR_NET_WRITE;
194 }
195
196 ats_free(req.ptr);
197 return TS_ERR_OKAY;
198 }
199
200 TSMgmtError
send_mgmt_error(int fd,OpType optype,TSMgmtError error)201 send_mgmt_error(int fd, OpType optype, TSMgmtError error)
202 {
203 MgmtMarshallInt ecode = error;
204 MgmtMarshallInt intval = 0;
205 MgmtMarshallData dataval = {nullptr, 0};
206 MgmtMarshallString strval = nullptr;
207
208 // Switch on operations, grouped by response format.
209 switch (optype) {
210 case OpType::BOUNCE:
211 case OpType::STOP:
212 case OpType::DRAIN:
213 case OpType::EVENT_RESOLVE:
214 case OpType::LIFECYCLE_MESSAGE:
215 case OpType::PROXY_STATE_SET:
216 case OpType::RECONFIGURE:
217 case OpType::RESTART:
218 case OpType::STATS_RESET_NODE:
219 case OpType::HOST_STATUS_UP:
220 case OpType::HOST_STATUS_DOWN:
221 case OpType::STORAGE_DEVICE_CMD_OFFLINE:
222 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 1);
223 return send_mgmt_response(fd, optype, &ecode);
224
225 case OpType::RECORD_SET:
226 case OpType::PROXY_STATE_GET:
227 case OpType::EVENT_ACTIVE:
228 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 2);
229 return send_mgmt_response(fd, optype, &ecode, &intval);
230
231 case OpType::EVENT_GET_MLT:
232 case OpType::SERVER_BACKTRACE:
233 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 2);
234 return send_mgmt_response(fd, optype, &ecode, &strval);
235
236 case OpType::RECORD_GET:
237 case OpType::RECORD_MATCH_GET:
238 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 5);
239 return send_mgmt_response(fd, optype, &ecode, &intval, &intval, &strval, &dataval);
240
241 case OpType::RECORD_DESCRIBE_CONFIG:
242 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 15);
243 return send_mgmt_response(fd, optype, &ecode, &strval /* name */, &dataval /* value */, &dataval /* default */,
244 &intval /* type */, &intval /* class */, &intval /* version */, &intval /* rsb */,
245 &intval /* order */, &intval /* access */, &intval /* update */, &intval /* updatetype */,
246 &intval /* checktype */, &intval /* source */, &strval /* checkexpr */);
247
248 case OpType::EVENT_REG_CALLBACK:
249 case OpType::EVENT_UNREG_CALLBACK:
250 case OpType::EVENT_NOTIFY:
251 case OpType::API_PING:
252 /* no response for these */
253 ink_release_assert(responses[static_cast<unsigned>(optype)].nfields == 0);
254 return TS_ERR_OKAY;
255
256 case OpType::UNDEFINED_OP:
257 return TS_ERR_OKAY;
258 }
259
260 // We should never get here unless OpTypes are added without
261 // updating the switch statement above. Don't do that; this
262 // code must be able to handle every OpType.
263
264 ink_fatal("missing generic error support for type %d management message", static_cast<int>(optype));
265 return TS_ERR_FAIL;
266 }
267
268 // Send a management message response. We don't need to worry about retransmitting the message if we get
269 // disconnected, so this is much simpler. We can directly marshall the response as a data object.
270 TSMgmtError
send_mgmt_response(int fd,OpType optype,...)271 send_mgmt_response(int fd, OpType optype, ...)
272 {
273 va_list ap;
274 MgmtMarshallInt msglen;
275 MgmtMarshallData reply = {nullptr, 0};
276 const MgmtMarshallType fields[] = {MGMT_MARSHALL_DATA};
277 const NetCmdOperation *cmd;
278
279 GETCMD(responses, optype, cmd);
280
281 va_start(ap, optype);
282 msglen = mgmt_message_length_v(cmd->fields, cmd->nfields, ap);
283 va_end(ap);
284
285 ink_assert(msglen >= 0);
286
287 reply.ptr = static_cast<char *>(ats_malloc(msglen));
288 reply.len = msglen;
289
290 // Marshall the message itself.
291 va_start(ap, optype);
292 if (mgmt_message_marshall_v(reply.ptr, reply.len, cmd->fields, cmd->nfields, ap) == -1) {
293 ats_free(reply.ptr);
294 va_end(ap);
295 return TS_ERR_PARAMS;
296 }
297
298 va_end(ap);
299
300 // Send the response as the payload of a data object.
301 if (mgmt_message_write(fd, fields, countof(fields), &reply) == -1) {
302 ats_free(reply.ptr);
303 return TS_ERR_NET_WRITE;
304 }
305
306 ats_free(reply.ptr);
307 return TS_ERR_OKAY;
308 }
309
310 template <unsigned N>
311 static TSMgmtError
recv_x(const struct NetCmdOperation (& ops)[N],void * buf,size_t buflen,OpType optype,va_list ap)312 recv_x(const struct NetCmdOperation (&ops)[N], void *buf, size_t buflen, OpType optype, va_list ap)
313 {
314 ssize_t msglen;
315 const NetCmdOperation *cmd;
316
317 GETCMD(ops, optype, cmd);
318
319 msglen = mgmt_message_parse_v(buf, buflen, cmd->fields, cmd->nfields, ap);
320 return (msglen == -1) ? TS_ERR_PARAMS : TS_ERR_OKAY;
321 }
322
323 TSMgmtError
recv_mgmt_request(void * buf,size_t buflen,OpType optype,...)324 recv_mgmt_request(void *buf, size_t buflen, OpType optype, ...)
325 {
326 TSMgmtError err;
327 va_list ap;
328
329 va_start(ap, optype);
330 err = recv_x(requests, buf, buflen, optype, ap);
331 va_end(ap);
332
333 return err;
334 }
335
336 TSMgmtError
recv_mgmt_response(void * buf,size_t buflen,OpType optype,...)337 recv_mgmt_response(void *buf, size_t buflen, OpType optype, ...)
338 {
339 TSMgmtError err;
340 va_list ap;
341
342 va_start(ap, optype);
343 err = recv_x(responses, buf, buflen, optype, ap);
344 va_end(ap);
345
346 return err;
347 }
348
349 TSMgmtError
recv_mgmt_message(int fd,MgmtMarshallData & msg)350 recv_mgmt_message(int fd, MgmtMarshallData &msg)
351 {
352 const MgmtMarshallType fields[] = {MGMT_MARSHALL_DATA};
353
354 if (mgmt_message_read(fd, fields, countof(fields), &msg) == -1) {
355 return TS_ERR_NET_READ;
356 }
357
358 return TS_ERR_OKAY;
359 }
360
361 OpType
extract_mgmt_request_optype(void * msg,size_t msglen)362 extract_mgmt_request_optype(void *msg, size_t msglen)
363 {
364 const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT};
365 MgmtMarshallInt optype;
366
367 if (mgmt_message_parse(msg, msglen, fields, countof(fields), &optype) == -1) {
368 return OpType::UNDEFINED_OP;
369 }
370
371 return static_cast<OpType>(optype);
372 }
373