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