1 /*
2     pmacct (Promiscuous mode IP Accounting package)
3     pmacct is Copyright (C) 2003-2020 by Paolo Lucente
4 */
5 
6 /*
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 
22 /* includes */
23 #include "pmacct.h"
24 #include "addr.h"
25 #if defined WITH_ZMQ
26 #include "zmq_common.h"
27 #endif
28 #include "bgp.h"
29 #include "bgp_lg.h"
30 #include "pmacct-data.h"
31 #include "thread_pool.h"
32 
33 /* global var */
34 thread_pool_t *bgp_lg_pool;
35 char bgp_lg_default_ip[] = "127.0.0.1";
36 
37 #if defined WITH_ZMQ
bgp_lg_wrapper()38 void bgp_lg_wrapper()
39 {
40   /* initialize variables */
41   if (!config.bgp_lg_ip) config.bgp_lg_ip = bgp_lg_default_ip;
42   if (!config.bgp_lg_port) config.bgp_lg_port = BGP_LG_DEFAULT_TCP_PORT;
43   if (!config.bgp_lg_threads) config.bgp_lg_threads = BGP_LG_DEFAULT_THREADS;
44 
45   /* initialize threads pool */
46   bgp_lg_pool = allocate_thread_pool(1);
47   assert(bgp_lg_pool);
48   Log(LOG_DEBUG, "DEBUG ( %s/core/lg ): Looking Glass thread initialized\n", config.name);
49 
50   /* giving a kick to the BGP thread */
51   send_to_pool(bgp_lg_pool, bgp_lg_daemon, NULL);
52 }
53 
bgp_lg_daemon()54 void bgp_lg_daemon()
55 {
56   char inproc_str[] = "inproc://lg_host_backend", log_id[SHORTBUFLEN];
57   struct p_zmq_host lg_host;
58 
59   memset(&lg_host, 0, sizeof(lg_host));
60 
61   snprintf(log_id, sizeof(log_id), "%s/core/lg", config.name);
62   p_zmq_set_log_id(&lg_host, log_id);
63 
64   p_zmq_set_address(&lg_host, inproc_str);
65   if (config.bgp_lg_user) p_zmq_set_username(&lg_host, config.bgp_lg_user);
66   if (config.bgp_lg_passwd) p_zmq_set_password(&lg_host, config.bgp_lg_passwd);
67 
68   p_zmq_router_setup(&lg_host, config.bgp_lg_ip, config.bgp_lg_port);
69   Log(LOG_INFO, "INFO ( %s/core/lg ): Looking Glass listening on %s:%u\n", config.name, config.bgp_lg_ip, config.bgp_lg_port);
70 
71   // XXX: more encodings supported in future?
72 #ifdef WITH_JANSSON
73   lg_host.router_worker.func = &bgp_lg_daemon_worker_json;
74 #else
75   lg_host.router_worker.func = NULL;
76   Log(LOG_WARNING, "WARN ( %s/core/lg ): Looking Glass depends on missing --enable-jansson.\n", config.name);
77   exit_gracefully(1);
78 #endif
79 
80   p_zmq_router_backend_setup(&lg_host, config.bgp_lg_threads);
81 }
82 
83 #ifdef WITH_JANSSON
bgp_lg_daemon_worker_json(void * zh,void * zs)84 void bgp_lg_daemon_worker_json(void *zh, void *zs)
85 {
86   struct p_zmq_host *lg_host = (struct p_zmq_host *) zh;
87   struct p_zmq_sock *sock = zs;
88   struct bgp_lg_req req;
89   struct bgp_lg_rep ipl_rep, gp_rep;
90   int ret;
91 
92   if (!lg_host || !sock) {
93     Log(LOG_ERR, "ERROR ( %s/core/lg ): bgp_lg_daemon_worker no lg_host or sock\nExiting.\n", config.name);
94     exit_gracefully(1);
95   }
96 
97   memset(&ipl_rep, 0, sizeof(ipl_rep));
98   memset(&gp_rep, 0, sizeof(gp_rep));
99 
100   for (;;) {
101     memset(&req, 0, sizeof(req));
102     ret = bgp_lg_daemon_decode_query_header_json(sock, &req);
103 
104     switch(req.type) {
105     case BGP_LG_QT_IP_LOOKUP:
106       {
107         struct bgp_lg_req_ipl_data query_data;
108 
109         req.data = &query_data;
110 	memset(req.data, 0, sizeof(struct bgp_lg_req_ipl_data));
111         ret = bgp_lg_daemon_decode_query_ip_lookup_json(sock, req.data);
112 
113         bgp_lg_rep_init(&ipl_rep);
114         if (!ret) ret = bgp_lg_daemon_ip_lookup(req.data, &ipl_rep, FUNC_TYPE_BGP);
115 
116         bgp_lg_daemon_encode_reply_ip_lookup_json(sock, &ipl_rep, ret);
117       }
118       break;
119     case BGP_LG_QT_GET_PEERS:
120       bgp_lg_rep_init(&gp_rep);
121       ret = bgp_lg_daemon_get_peers(&gp_rep, FUNC_TYPE_BGP);
122       bgp_lg_daemon_encode_reply_get_peers_json(sock, &gp_rep, ret);
123 
124       break;
125     case BGP_LG_QT_UNKNOWN:
126     default:
127       bgp_lg_daemon_encode_reply_unknown_json(sock);
128       break;
129     }
130   }
131 }
132 
bgp_lg_daemon_decode_query_header_json(struct p_zmq_sock * sock,struct bgp_lg_req * req)133 int bgp_lg_daemon_decode_query_header_json(struct p_zmq_sock *sock, struct bgp_lg_req *req)
134 {
135   json_t *req_obj, *query_type_json, *queries_num_json;
136   json_error_t req_err;
137   char *req_str;
138   int ret = SUCCESS;
139 
140   if (!sock || !req) return ERR;
141 
142   req_str = p_zmq_recv_str(sock);
143   if (req_str) {
144     req_obj = json_loads(req_str, 0, &req_err);
145     free(req_str);
146   }
147   else {
148     req_obj = NULL;
149     ret = ERR;
150   }
151 
152   if (req_obj) {
153     if (!json_is_object(req_obj)) {
154       Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_header_json(): json_is_object() failed.\n", config.name);
155       ret = ERR;
156       goto exit_lane;
157     }
158     else {
159       query_type_json = json_object_get(req_obj, "query_type");
160       if (query_type_json == NULL) {
161         Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_header_json(): no 'query_type' element.\n", config.name);
162         ret = ERR;
163         goto exit_lane;
164       }
165       else req->type = json_integer_value(query_type_json);
166 
167       queries_num_json = json_object_get(req_obj, "queries");
168       if (queries_num_json == NULL) {
169         Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_header_json(): no 'queries' element.\n", config.name);
170         ret = ERR;
171         goto exit_lane;
172       }
173       else req->num = json_integer_value(queries_num_json);
174 
175       /* XXX: only one query per query message currently supported */
176       if (req->num != 1) {
177         Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_header_json(): 'queries' element != 1.\n", config.name);
178         ret = ERR;
179         goto exit_lane;
180       }
181     }
182 
183     exit_lane:
184     json_decref(req_obj);
185   }
186   else {
187     Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_header_json(): invalid request received: %s.\n", config.name, req_err.text);
188     ret = ERR;
189   }
190 
191   return ret;
192 }
193 
bgp_lg_daemon_decode_query_ip_lookup_json(struct p_zmq_sock * sock,struct bgp_lg_req_ipl_data * req)194 int bgp_lg_daemon_decode_query_ip_lookup_json(struct p_zmq_sock *sock, struct bgp_lg_req_ipl_data *req)
195 {
196   json_error_t req_err;
197   json_t *req_obj, *peer_ip_src_json, *bgp_port_json, *ip_prefix_json, *rd_json;
198   const char *peer_ip_src_str, *ip_prefix_str, *rd_str;
199   char *req_str;
200   int ret = SUCCESS;
201   struct rd_as4 *rd_as4_ptr;
202   u_int16_t bgp_port = 0;
203 
204   if (!sock || !req) return ERR;
205 
206   req_str = p_zmq_recv_str(sock);
207 
208   if (req_str) {
209     req_obj = json_loads(req_str, 0, &req_err);
210     free(req_str);
211   }
212   else {
213     req_obj = NULL;
214     ret = ERR;
215   }
216 
217   if (req_obj) {
218     if (!json_is_object(req_obj)) {
219       Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): json_is_object() failed.\n", config.name);
220       ret = ERR;
221       goto exit_lane;
222     }
223     else {
224       bgp_port_json = json_object_get(req_obj, "peer_tcp_port");
225       if (bgp_port_json) {
226         int bgp_port_int;
227 
228         bgp_port_int = json_integer_value(bgp_port_json);
229         if (bgp_port_int >= 0 && bgp_port_int <= 65535) bgp_port = bgp_port_int;
230         else {
231           Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): bogus 'peer_tcp_port' element.\n", config.name);
232           ret = ERR;
233           goto exit_lane;
234         }
235       }
236 
237       peer_ip_src_json = json_object_get(req_obj, "peer_ip_src");
238       if (peer_ip_src_json == NULL) {
239 	Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): no 'peer_ip_src' element.\n", config.name);
240 	ret = ERR;
241 	goto exit_lane;
242       }
243       else {
244 	struct host_addr peer_ip_src_ha;
245 
246 	peer_ip_src_str = json_string_value(peer_ip_src_json);
247 	str_to_addr(peer_ip_src_str, &peer_ip_src_ha);
248 	addr_to_sa(&req->peer, &peer_ip_src_ha, bgp_port);
249 	if (!req->peer.sa_family) {
250 	  Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): bogus 'peer_ip_src' element.\n", config.name);
251 	  ret = ERR;
252 	  goto exit_lane;
253 	}
254       }
255 
256       ip_prefix_json = json_object_get(req_obj, "ip_prefix");
257       if (ip_prefix_json == NULL) {
258 	Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): no 'ip_prefix' element.\n", config.name);
259 	ret = ERR;
260 	goto exit_lane;
261       }
262       else {
263 	ip_prefix_str = json_string_value(ip_prefix_json);
264 	str2prefix(ip_prefix_str, &req->pref);
265 	if (!req->pref.family) {
266 	  Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): bogus 'ip_prefix' element.\n", config.name);
267 	  ret = ERR;
268 	  goto exit_lane;
269 	}
270       }
271 
272       rd_json = json_object_get(req_obj, "rd");
273       if (rd_json) {
274         rd_str = json_string_value(rd_json);
275         bgp_str2rd(&req->rd, (char *) rd_str);
276 	rd_as4_ptr = (struct rd_as4 *) &req->rd;
277         if (!rd_as4_ptr->as) {
278           Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): bogus 'rd' element.\n", config.name);
279           ret = ERR;
280           goto exit_lane;
281         }
282       }
283     }
284 
285     exit_lane:
286     json_decref(req_obj);
287   }
288   else {
289     Log(LOG_WARNING, "WARN ( %s/core/lg ): bgp_lg_daemon_decode_query_ip_lookup_json(): invalid request received: %s.\n", config.name, req_err.text);
290     ret = ERR;
291   }
292 
293   return ret;
294 }
295 
bgp_lg_daemon_encode_reply_results_json(struct p_zmq_sock * sock,struct bgp_lg_rep * rep,int res,int query_type)296 void bgp_lg_daemon_encode_reply_results_json(struct p_zmq_sock *sock, struct bgp_lg_rep *rep, int res, int query_type)
297 {
298   json_t *rep_results_obj;
299   char *rep_results_str;
300 
301   rep_results_obj = json_object();
302   json_object_set_new_nocheck(rep_results_obj, "results", json_integer(rep->results));
303   json_object_set_new_nocheck(rep_results_obj, "query_type", json_integer(query_type));
304 
305   if (!rep->results && res) {
306     switch (res) {
307     case BGP_LOOKUP_ERR:
308       json_object_set_new_nocheck(rep_results_obj, "text", json_string("lookup error"));
309       break;
310     case BGP_LOOKUP_NOPREFIX:
311       json_object_set_new_nocheck(rep_results_obj, "text", json_string("prefix not found"));
312       break;
313     case BGP_LOOKUP_NOPEER:
314       json_object_set_new_nocheck(rep_results_obj, "text", json_string("peer not found"));
315       break;
316     default:
317       json_object_set_new_nocheck(rep_results_obj, "text", json_string("unknown lookup error"));
318       break;
319     }
320   }
321 
322   rep_results_str = json_dumps(rep_results_obj, JSON_PRESERVE_ORDER);
323   json_decref(rep_results_obj);
324 
325   if (rep_results_str) {
326     if (!rep->results) p_zmq_send_str(sock, rep_results_str);
327     else p_zmq_sendmore_str(sock, rep_results_str);
328 
329     free(rep_results_str);
330   }
331 }
332 
bgp_lg_daemon_encode_reply_ip_lookup_json(struct p_zmq_sock * sock,struct bgp_lg_rep * rep,int res)333 void bgp_lg_daemon_encode_reply_ip_lookup_json(struct p_zmq_sock *sock, struct bgp_lg_rep *rep, int res)
334 {
335   if (!sock || !rep) return;
336 
337   bgp_lg_daemon_encode_reply_results_json(sock, rep, res, BGP_LG_QT_IP_LOOKUP);
338 
339   if (rep->results) {
340     struct bgp_lg_rep_data *data;
341     struct bgp_lg_rep_ipl_data *ipl_data;
342     char *rep_data_str;
343     u_int32_t idx;
344 
345     for (idx = 0, data = rep->data; idx < rep->results; idx++) {
346       ipl_data = data->ptr;
347 
348       rep_data_str = bgp_lg_daemon_encode_reply_ip_lookup_data_json(ipl_data);
349 
350       if (rep_data_str) {
351 	if (idx == (rep->results - 1)) p_zmq_send_str(sock, rep_data_str);
352         else p_zmq_sendmore_str(sock, rep_data_str);
353 
354 	free(rep_data_str);
355       }
356 
357       data = data->next;
358     }
359   }
360 }
361 
bgp_lg_daemon_encode_reply_ip_lookup_data_json(struct bgp_lg_rep_ipl_data * rep_data)362 char *bgp_lg_daemon_encode_reply_ip_lookup_data_json(struct bgp_lg_rep_ipl_data *rep_data)
363 {
364   struct bgp_node dummy_node;
365   char event_type[] = "lglass", *data_str = NULL;
366 
367   if (rep_data && rep_data->pref) {
368     memset(&dummy_node, 0, sizeof(dummy_node));
369     memcpy(&dummy_node.p, rep_data->pref, sizeof(struct prefix));
370 
371     bgp_peer_log_msg(&dummy_node, rep_data->info, rep_data->afi, rep_data->safi, event_type,
372 		     PRINT_OUTPUT_JSON, &data_str, BGP_LOG_TYPE_MISC);
373   }
374 
375   return data_str;
376 }
377 
bgp_lg_daemon_encode_reply_get_peers_json(struct p_zmq_sock * sock,struct bgp_lg_rep * rep,int res)378 void bgp_lg_daemon_encode_reply_get_peers_json(struct p_zmq_sock *sock, struct bgp_lg_rep *rep, int res)
379 {
380   if (!sock || !rep) return;
381 
382   bgp_lg_daemon_encode_reply_results_json(sock, rep, res, BGP_LG_QT_GET_PEERS);
383 
384   if (rep->results) {
385     struct bgp_lg_rep_data *data;
386     struct bgp_lg_rep_gp_data *gp_data;
387     char *rep_data_str;
388     u_int32_t idx;
389 
390     for (idx = 0, data = rep->data; idx < rep->results; idx++) {
391       gp_data = data->ptr;
392 
393       rep_data_str = bgp_lg_daemon_encode_reply_get_peers_data_json(gp_data);
394 
395       if (rep_data_str) {
396         if (idx == (rep->results - 1)) p_zmq_send_str(sock, rep_data_str);
397         else p_zmq_sendmore_str(sock, rep_data_str);
398 
399         free(rep_data_str);
400       }
401 
402       data = data->next;
403     }
404   }
405 }
406 
bgp_lg_daemon_encode_reply_get_peers_data_json(struct bgp_lg_rep_gp_data * rep_data)407 char *bgp_lg_daemon_encode_reply_get_peers_data_json(struct bgp_lg_rep_gp_data *rep_data)
408 {
409   struct bgp_misc_structs *bms;
410   char ip_address[INET6_ADDRSTRLEN], *data_str = NULL;
411   json_t *obj = json_object();
412 
413   if (rep_data) {
414     struct bgp_peer *peer = rep_data->peer;
415 
416     bms = bgp_select_misc_db(peer->type);
417     if (!bms) return NULL;
418 
419     addr_to_str(ip_address, &peer->addr);
420     json_object_set_new_nocheck(obj, bms->peer_str, json_string(ip_address));
421 
422     addr_to_str(ip_address, &peer->id);
423     json_object_set_new_nocheck(obj, "peer_id", json_string(ip_address));
424 
425     json_object_set_new_nocheck(obj, "peer_tcp_port", json_integer((json_int_t)peer->tcp_port));
426     json_object_set_new_nocheck(obj, "peer_as", json_integer((json_int_t)peer->as));
427 
428     data_str = compose_json_str(obj);
429   }
430 
431   return data_str;
432 }
433 
bgp_lg_daemon_encode_reply_unknown_json(struct p_zmq_sock * sock)434 void bgp_lg_daemon_encode_reply_unknown_json(struct p_zmq_sock *sock)
435 {
436   json_t *rep_results_obj;
437   char *rep_results_str;
438 
439   if (!sock) return;
440 
441   rep_results_obj = json_object();
442   json_object_set_new_nocheck(rep_results_obj, "results", json_integer(FALSE));
443   json_object_set_new_nocheck(rep_results_obj, "query_type", json_integer(BGP_LG_QT_UNKNOWN));
444   json_object_set_new_nocheck(rep_results_obj, "text", json_string("unsupported query_type"));
445 
446   rep_results_str = json_dumps(rep_results_obj, JSON_PRESERVE_ORDER);
447   json_decref(rep_results_obj);
448 
449   p_zmq_send_str(sock, rep_results_str);
450 }
451 #endif
452 #endif /* WITH_ZMQ */
453