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 "pmacct-dlt.h"
25 #include "addr.h"
26 #include "isis.h"
27 #include "thread_pool.h"
28 
29 #include "stream.h"
30 #include "hash.h"
31 #include "prefix.h"
32 
33 #include "dict.h"
34 #include "thread.h"
35 #include "iso.h"
36 #include "table.h"
37 #include "isis_constants.h"
38 #include "isis_common.h"
39 #include "isis_adjacency.h"
40 #include "isis_circuit.h"
41 #include "isis_network.h"
42 #include "isis_misc.h"
43 #include "isis_flags.h"
44 #include "isis_tlv.h"
45 #include "isisd.h"
46 #include "isis_dynhn.h"
47 #include "isis_lsp.h"
48 #include "isis_pdu.h"
49 #include "iso_checksum.h"
50 #include "isis_csm.h"
51 #include "isis_events.h"
52 #include "isis_spf.h"
53 #include "isis_route.h"
54 
55 /* variables to be exported away */
56 thread_pool_t *isis_pool;
57 
58 /* Functions */
nfacctd_isis_wrapper()59 void nfacctd_isis_wrapper()
60 {
61   /* initialize threads pool */
62   isis_pool = allocate_thread_pool(1);
63   assert(isis_pool);
64   Log(LOG_DEBUG, "DEBUG ( %s/core/ISIS ): %d thread(s) initialized\n", config.name, 1);
65 
66   /* giving a kick to the BGP thread */
67   send_to_pool(isis_pool, skinny_isis_daemon, NULL);
68 }
69 
skinny_isis_daemon()70 void skinny_isis_daemon()
71 {
72   char errbuf[PCAP_ERRBUF_SIZE];
73   struct pm_pcap_device device;
74   struct pm_pcap_isis_callback_data cb_data;
75   struct host_addr addr;
76   struct prefix_ipv4 *ipv4;
77   struct plugin_requests req;
78   int index, ret;
79 
80   char area_tag[] = "default";
81   struct isis_area *area;
82   struct isis_circuit *circuit;
83   struct interface interface;
84 
85   memset(&device, 0, sizeof(struct pm_pcap_device));
86   memset(&cb_data, 0, sizeof(cb_data));
87   memset(&interface, 0, sizeof(interface));
88   memset(&isis_spf_deadline, 0, sizeof(isis_spf_deadline));
89 
90   memset(&ime, 0, sizeof(ime));
91   memset(&req, 0, sizeof(req));
92   reload_map = FALSE;
93   glob_isis_seq_num = 0;
94 
95   /* initializing IS-IS structures */
96   isis = NULL;
97   isis_init();
98   dyn_cache_init();
99 
100   /* thread master */
101   master = thread_master_create();
102 
103   if (!config.nfacctd_isis_iface && !config.igp_daemon_map) {
104     Log(LOG_ERR, "ERROR ( %s/core/ISIS ): No 'isis_daemon_iface' and 'igp_daemon_map' values specified. Terminating thread.\n", config.name);
105     exit_gracefully(1);
106   }
107   else if (config.nfacctd_isis_iface && config.igp_daemon_map) {
108     Log(LOG_ERR, "ERROR ( %s/core/ISIS ): 'isis_daemon_iface' and 'igp_daemon_map' are mutually exclusive. Terminating thread.\n", config.name);
109     exit_gracefully(1);
110   }
111 
112   if (config.nfacctd_isis_iface) {
113     if ((device.dev_desc = pcap_open_live(config.nfacctd_isis_iface, 65535, 0, 1000, errbuf)) == NULL) {
114       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): pcap_open_live(): %s\n", config.name, errbuf);
115       exit_gracefully(1);
116     }
117 
118     device.link_type = pcap_datalink(device.dev_desc);
119     for (index = 0; _isis_devices[index].link_type != -1; index++) {
120       if (device.link_type == _isis_devices[index].link_type)
121         device.data = &_isis_devices[index];
122     }
123 
124     if (device.data == NULL) {
125       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): data link not supported: %d\n", config.name, device.link_type);
126       return;
127     }
128     else {
129       Log(LOG_INFO, "OK ( %s/core/ISIS ): link type is: %d\n", config.name, device.link_type);
130       cb_data.device = &device;
131     }
132   }
133 
134   if (config.igp_daemon_map) {
135     int igp_map_allocated = FALSE;
136 
137     glob_isis_seq_num++;
138     req.key_value_table = (void *) &ime;
139     memset(&sysid_fragment_table, 0, sizeof(sysid_fragment_table));
140     load_id_file(MAP_IGP, config.igp_daemon_map, NULL, &req, &igp_map_allocated);
141   }
142 
143   area = isis_area_create();
144   area->area_tag = area_tag;
145   area->is_type = IS_LEVEL_2;
146   area->newmetric = TRUE;
147   pm_listnode_add(isis->area_list, area);
148   Log(LOG_DEBUG, "DEBUG ( %s/core/ISIS ): New IS-IS area instance %s\n", config.name, area->area_tag);
149   if (config.nfacctd_isis_net) area_net_title(area, config.nfacctd_isis_net);
150   else {
151     Log(LOG_ERR, "ERROR ( %s/core/ISIS ): 'isis_daemon_net' value is not specified. Terminating thread.\n", config.name);
152     exit_gracefully(1);
153   }
154 
155   circuit = isis_circuit_new();
156   circuit->circ_type = CIRCUIT_T_P2P;
157   if (config.nfacctd_isis_iface) {
158     circuit->fd = pcap_fileno(device.dev_desc);
159     circuit->tx = isis_send_pdu_p2p;
160   }
161   else {
162     circuit->fd = 0;
163     circuit->tx = NULL;
164   }
165   circuit->interface = &interface;
166   circuit->state = C_STATE_UP;
167 
168   if (config.nfacctd_isis_ip) {
169     trim_spaces(config.nfacctd_isis_ip);
170     ret = str_to_addr(config.nfacctd_isis_ip, &addr);
171     if (!ret) {
172       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): 'isis_daemon_ip' value is not a valid IPv4/IPv6 address. Terminating thread.\n", config.name);
173       exit_gracefully(1);
174     }
175   }
176   else {
177     Log(LOG_ERR, "ERROR ( %s/core/ISIS ): 'isis_daemon_ip' value is not specified. Terminating thread.\n", config.name);
178     exit_gracefully(1);
179   }
180 
181   circuit->ip_router = addr.address.ipv4.s_addr;
182   ipv4 = isis_prefix_ipv4_new();
183   ipv4->prefixlen = 32;
184   ipv4->prefix.s_addr = addr.address.ipv4.s_addr;
185   circuit->ip_addrs = pm_list_new();
186   pm_listnode_add(circuit->ip_addrs, ipv4);
187 
188   circuit_update_nlpids(circuit);
189   isis_circuit_configure(circuit, area);
190   cb_data.circuit = circuit;
191 
192   area->ip_circuits = 1;
193   if (config.nfacctd_isis_iface) {
194     memcpy(circuit->interface->name, config.nfacctd_isis_iface, strlen(config.nfacctd_isis_iface));
195     circuit->interface->ifindex = if_nametoindex(config.nfacctd_isis_iface);
196   }
197   else {
198     // XXX
199   }
200 
201   if (!config.nfacctd_isis_mtu) config.nfacctd_isis_mtu = SNAPLEN_ISIS_DEFAULT;
202 
203   if (config.nfacctd_isis_iface) {
204     for (;;) {
205       /* XXX: should get a select() here at some stage? */
206       pcap_loop(device.dev_desc, -1, isis_pdu_runner, (u_char *) &cb_data);
207 
208       break;
209     }
210 
211     pcap_close(device.dev_desc);
212   }
213   else if (config.igp_daemon_map) {
214     for (;;) {
215       sleep(3);
216 
217       if (reload_map) {
218         int igp_map_allocated = FALSE;
219 
220 	glob_isis_seq_num++;
221 	memset(&sysid_fragment_table, 0, sizeof(sysid_fragment_table));
222         load_id_file(MAP_IGP, config.igp_daemon_map, NULL, &req, &igp_map_allocated);
223 	reload_map = FALSE;
224       }
225     }
226   }
227 }
228 
isis_pdu_runner(u_char * user,const struct pcap_pkthdr * pkthdr,const u_char * buf)229 void isis_pdu_runner(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *buf)
230 {
231   struct pm_pcap_isis_callback_data *cb_data = (struct pm_pcap_isis_callback_data *) user;
232   struct pm_pcap_device *device = cb_data->device;
233   struct isis_circuit *circuit = cb_data->circuit;
234   struct packet_ptrs pptrs;
235   int ret;
236 
237   struct stream stm;
238   u_char *ssnpa;
239 
240   /* Let's export a time reference */
241   memcpy(&isis_now, &pkthdr->ts, sizeof(struct timeval));
242 
243   /* check if we have to expire adjacency first */
244   if (circuit && circuit->u.p2p.neighbor) {
245     if (timeval_cmp(&isis_now, &circuit->u.p2p.neighbor->expire) >= 0)
246       isis_adj_expire(circuit->u.p2p.neighbor);
247   }
248 
249   memset(&pptrs, 0, sizeof(pptrs));
250   memset(&stm, 0, sizeof(stm));
251 
252   if (buf) {
253     pptrs.pkthdr = (struct pcap_pkthdr *) pkthdr;
254     pptrs.packet_ptr = (u_char *) buf;
255 
256     (*device->data->handler)(pkthdr, &pptrs);
257     if (pptrs.iph_ptr) {
258       if ((*pptrs.l3_handler)(&pptrs)) {
259 
260 	/*assembling handover to isis_handle_pdu() */
261 	stm.data = pptrs.iph_ptr;
262 	stm.getp = 0;
263 	stm.endp = pkthdr->caplen - (pptrs.iph_ptr - pptrs.packet_ptr);
264 	stm.size = pkthdr->caplen - (pptrs.iph_ptr - pptrs.packet_ptr);
265 	ssnpa = pptrs.packet_ptr;
266 	circuit->rcv_stream = &stm;
267 	circuit->interface->mtu = config.nfacctd_isis_mtu;
268 
269 	/* process IS-IS packet */
270 	isis_handle_pdu (circuit, ssnpa);
271       }
272     }
273   }
274 
275   /* check if it's time to run SPF */
276   if (timeval_cmp(&isis_now, &isis_spf_deadline) >= 0) {
277     if (circuit->area->is_type & IS_LEVEL_1) {
278       if (circuit->area->ip_circuits) {
279 	ret = isis_run_spf(circuit->area, 1, AF_INET);
280 	isis_route_validate_table (circuit->area, circuit->area->route_table[0]);
281       }
282       /* XXX: IPv6 handled here */
283     }
284 
285     if (circuit->area->is_type & IS_LEVEL_2) {
286       if (circuit->area->ip_circuits) {
287 	ret = isis_run_spf(circuit->area, 2, AF_INET);
288         (void)ret; //TODO treat error
289 	isis_route_validate_table (circuit->area, circuit->area->route_table[1]);
290       }
291       /* XXX: IPv6 handled here */
292     }
293 
294     isis_route_validate_merge (circuit->area, AF_INET);
295 
296     dyn_cache_cleanup();
297 
298     isis_spf_deadline.tv_sec = isis_now.tv_sec + isis_jitter(PERIODIC_SPF_INTERVAL, 10);
299     isis_spf_deadline.tv_usec = 0;
300   }
301 
302   if (timeval_cmp(&isis_now, &isis_psnp_deadline) >= 0) {
303     send_psnp(1, circuit);
304     send_psnp(2, circuit);
305 
306     isis_psnp_deadline.tv_sec = isis_now.tv_sec + isis_jitter(PSNP_INTERVAL, PSNP_JITTER);
307     isis_psnp_deadline.tv_usec = 0;
308   }
309 }
310 
isis_sll_handler(const struct pcap_pkthdr * h,register struct packet_ptrs * pptrs)311 void isis_sll_handler(const struct pcap_pkthdr *h, register struct packet_ptrs *pptrs)
312 {
313   register const struct sll_header *sllp;
314   u_int caplen = h->caplen;
315   u_int16_t etype, nl;
316   u_char *p;
317 
318   if (caplen < SLL_HDR_LEN) {
319     pptrs->iph_ptr = NULL;
320     return;
321   }
322 
323   p = pptrs->packet_ptr;
324   (void)p; //TODO treat error
325 
326   sllp = (const struct sll_header *) pptrs->packet_ptr;
327   etype = ntohs(sllp->sll_protocol);
328   nl = SLL_HDR_LEN;
329 
330   if (etype == ETHERTYPE_GRE_ISO) {
331     pptrs->l3_proto = ETHERTYPE_GRE_ISO;
332     pptrs->l3_handler = iso_handler;
333     pptrs->iph_ptr = (u_char *)(pptrs->packet_ptr + nl);
334     return;
335   }
336 
337   pptrs->l3_proto = 0;
338   pptrs->l3_handler = NULL;
339   pptrs->iph_ptr = NULL;
340 }
341 
iso_handler(register struct packet_ptrs * pptrs)342 int iso_handler(register struct packet_ptrs *pptrs)
343 {
344   return FALSE;
345 }
346 
isis_srcdst_lookup(struct packet_ptrs * pptrs)347 void isis_srcdst_lookup(struct packet_ptrs *pptrs)
348 {
349   struct route_node *result;
350   struct isis_area *area;
351   char area_tag[] = "default";
352   int level;
353   struct in_addr pref4;
354   struct in6_addr pref6;
355 
356   pptrs->igp_src = NULL;
357   pptrs->igp_dst = NULL;
358   pptrs->igp_src_info = NULL;
359   pptrs->igp_dst_info = NULL;
360 
361   area = isis_area_lookup(area_tag);
362 
363   if (area) {
364     level = MAX(area->is_type, 2);
365 
366     if (pptrs->l3_proto == ETHERTYPE_IP) {
367       if (!pptrs->igp_src) {
368 	memcpy(&pref4, &((struct pm_iphdr *)pptrs->iph_ptr)->ip_src, sizeof(struct in_addr));
369 	result = route_node_match_ipv4(area->route_table[level-1], &pref4);
370 
371 	if (result) {
372 	  pptrs->igp_src = (char *) &result->p;
373 	  pptrs->igp_src_info = (char *) result->info;
374 	  if (result->p.prefixlen > pptrs->lm_mask_src) {
375 	    pptrs->lm_mask_src = result->p.prefixlen;
376 	    pptrs->lm_method_src = NF_NET_IGP;
377 	  }
378 	}
379       }
380 
381       if (!pptrs->igp_dst) {
382 	memcpy(&pref4, &((struct pm_iphdr *)pptrs->iph_ptr)->ip_dst, sizeof(struct in_addr));
383 	result = route_node_match_ipv4(area->route_table[level-1], &pref4);
384 
385 	if (result) {
386 	  pptrs->igp_dst = (char *) &result->p;
387 	  pptrs->igp_dst_info = (char *) result->info;
388           if (result->p.prefixlen > pptrs->lm_mask_dst) {
389             pptrs->lm_mask_dst = result->p.prefixlen;
390             pptrs->lm_method_dst = NF_NET_IGP;
391           }
392 	}
393       }
394     }
395     else if (area && pptrs->l3_proto == ETHERTYPE_IPV6) {
396       if (!pptrs->igp_src) {
397         memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_src, sizeof(struct in6_addr));
398         result = route_node_match_ipv6(area->route_table6[level-1], &pref6);
399 
400         if (result) {
401           pptrs->igp_src = (char *) &result->p;
402           pptrs->igp_src_info = (char *) result->info;
403           if (result->p.prefixlen > pptrs->lm_mask_src) {
404             pptrs->lm_mask_src = result->p.prefixlen;
405             pptrs->lm_method_src = NF_NET_IGP;
406           }
407         }
408       }
409 
410       if (!pptrs->igp_dst) {
411         memcpy(&pref6, &((struct ip6_hdr *)pptrs->iph_ptr)->ip6_dst, sizeof(struct in6_addr));
412         result = route_node_match_ipv6(area->route_table6[level-1], &pref6);
413 
414 	if (result) {
415 	  pptrs->igp_dst = (char *) &result->p;
416 	  pptrs->igp_dst_info = (char *) result->info;
417           if (result->p.prefixlen > pptrs->lm_mask_dst) {
418             pptrs->lm_mask_dst = result->p.prefixlen;
419             pptrs->lm_method_dst = NF_NET_IGP;
420           }
421 	}
422       }
423     }
424   }
425 }
426 
igp_daemon_map_node_handler(char * filename,struct id_entry * e,char * value,struct plugin_requests * req,int acct_type)427 int igp_daemon_map_node_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type)
428 {
429   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
430 
431   if (!str_to_addr(value, &entry->node) || entry->node.family != AF_INET) {
432     Log(LOG_ERR, "ERROR ( %s ): Bad IPv4 address '%s'. ", filename, value);
433     return TRUE;
434   }
435 
436   return FALSE;
437 }
438 
igp_daemon_map_area_id_handler(char * filename,struct id_entry * e,char * value,struct plugin_requests * req,int acct_type)439 int igp_daemon_map_area_id_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type)
440 {
441   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
442   int x = 0, j, len;
443 
444   len = strlen(value);
445 
446   while (x < len) {
447     if (!isdigit(value[x])) {
448       Log(LOG_ERR, "ERROR ( %s ): Bad 'area_id' value: '%s'. ", filename, value);
449       return TRUE;
450     }
451     x++;
452   }
453 
454   j = atoi(value);
455   if (j < 0 || j > 65535) {
456     Log(LOG_ERR, "ERROR ( %s ): Bad 'area_id' value (range: 0 >= value > 65536). ", filename);
457     return TRUE;
458   }
459 
460   entry->area_id = j;
461 
462   return FALSE;
463 }
464 
igp_daemon_map_adj_metric_handler(char * filename,struct id_entry * e,char * value,struct plugin_requests * req,int acct_type)465 int igp_daemon_map_adj_metric_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type)
466 {
467   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
468   char *str_ptr, *token, *sep, *ip_str, *metric_str, *endptr;
469   int idx = 0;
470 
471   str_ptr = strdup(value);
472   if (!str_ptr) {
473     Log(LOG_ERR, "ERROR ( %s ): not enough memory to strdup(). ", filename);
474     return TRUE;
475   }
476 
477   while ((token = extract_token(&str_ptr, ';'))) {
478     if (idx >= MAX_IGP_MAP_ELEM) {
479       Log(LOG_ERR, "ERROR ( %s ): maximum number of elements (%u) per adj_metric violated. ", filename, MAX_IGP_MAP_ELEM);
480       return TRUE;
481     }
482 
483     sep = strchr(token, ',');
484     if (!sep) {
485       Log(LOG_WARNING, "WARN ( %s ): missing adj_metric entry separator '%s'.\n", filename, token);
486       continue;
487     }
488 
489     ip_str = token;
490     metric_str = sep+1;
491     *sep = '\0';
492 
493     if (!isis_str2prefix(ip_str, &entry->adj_metric[idx].prefix) || entry->adj_metric[idx].prefix.family != AF_INET) {
494       Log(LOG_WARNING, "WARN ( %s ): Bad IPv4 address '%s'.\n", filename, ip_str);
495       continue;
496     }
497 
498     entry->adj_metric[idx].metric = strtoull(metric_str, &endptr, 10);
499     if (!entry->adj_metric[idx].metric) {
500       Log(LOG_WARNING, "WARN ( %s ): Bad metric '%s'.\n", filename, metric_str);
501       continue;
502     }
503 
504     idx++;
505   }
506 
507   if (!idx) {
508     Log(LOG_ERR, "ERROR ( %s ): invalid or empty adj_metric entry '%s'. ", filename, value);
509     return TRUE;
510   }
511   else entry->adj_metric_num = idx;
512 
513   return FALSE;
514 }
515 
igp_daemon_map_reach_metric_handler(char * filename,struct id_entry * e,char * value,struct plugin_requests * req,int acct_type)516 int igp_daemon_map_reach_metric_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type)
517 {
518   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
519   char *str_ptr, *token, *sep, *ip_str, *metric_str, *endptr;
520   int idx = 0;
521 
522   str_ptr = strdup(value);
523   if (!str_ptr) {
524     Log(LOG_ERR, "ERROR ( %s ): not enough memory to strdup(). ", filename);
525     return TRUE;
526   }
527 
528   while ((token = extract_token(&str_ptr, ';'))) {
529     if (idx >= MAX_IGP_MAP_ELEM) {
530       Log(LOG_ERR, "ERROR ( %s ): maximum number of elements (%u) per reach_metric violated. ", filename, MAX_IGP_MAP_ELEM);
531       return TRUE;
532     }
533 
534     sep = strchr(token, ',');
535     if (!sep) {
536       Log(LOG_WARNING, "WARN ( %s ): missing reach_metric entry separator '%s'.\n", filename, token);
537       continue;
538     }
539 
540     ip_str = token;
541     metric_str = sep+1;
542     *sep = '\0';
543 
544     if (!isis_str2prefix(ip_str, &entry->reach_metric[idx].prefix) || entry->reach_metric[idx].prefix.family != AF_INET) {
545       Log(LOG_WARNING, "WARN ( %s ): Bad IPv4 address '%s'.\n", filename, ip_str);
546       continue;
547     }
548 
549     entry->reach_metric[idx].metric = strtoull(metric_str, &endptr, 10);
550     if (!entry->reach_metric[idx].metric) {
551       Log(LOG_WARNING, "WARN ( %s ): Bad metric '%s'.\n", filename, metric_str);
552       continue;
553     }
554 
555     idx++;
556   }
557 
558   if (!idx) {
559     Log(LOG_ERR, "ERROR ( %s ): invalid or empty reach_metric entry '%s'. ", filename, value);
560     return TRUE;
561   }
562   else entry->reach_metric_num = idx;
563 
564   return FALSE;
565 }
566 
igp_daemon_map_reach6_metric_handler(char * filename,struct id_entry * e,char * value,struct plugin_requests * req,int acct_type)567 int igp_daemon_map_reach6_metric_handler(char *filename, struct id_entry *e, char *value, struct plugin_requests *req, int acct_type)
568 {
569   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
570   char *str_ptr, *token, *sep, *ip_str, *metric_str, *endptr;
571   int idx = 0;
572 
573   str_ptr = strdup(value);
574   if (!str_ptr) {
575     Log(LOG_ERR, "ERROR ( %s ): not enough memory to strdup(). ", filename);
576     return TRUE;
577   }
578 
579   while ((token = extract_token(&str_ptr, ';'))) {
580     if (idx >= MAX_IGP_MAP_ELEM) {
581       Log(LOG_ERR, "ERROR ( %s ): maximum number of elements (%u) per reach6_metric violated. ", filename, MAX_IGP_MAP_ELEM);
582       return TRUE;
583     }
584 
585     sep = strchr(token, ',');
586     if (!sep) {
587       Log(LOG_WARNING, "WARN ( %s ): missing reach6_metric entry separator '%s'.\n", filename, token);
588       continue;
589     }
590 
591     ip_str = token;
592     metric_str = sep+1;
593     *sep = '\0';
594 
595     if (!isis_str2prefix(ip_str, &entry->reach6_metric[idx].prefix) || entry->reach6_metric[idx].prefix.family != AF_INET6) {
596       Log(LOG_WARNING, "WARN ( %s ): Bad IPv6 address '%s'.\n", filename, ip_str);
597       continue;
598     }
599 
600     entry->reach6_metric[idx].metric = strtoull(metric_str, &endptr, 10);
601     if (!entry->reach6_metric[idx].metric) {
602       Log(LOG_WARNING, "WARN ( %s ): Bad metric '%s'.\n", filename, metric_str);
603       continue;
604     }
605 
606     idx++;
607   }
608 
609   if (!idx) {
610     Log(LOG_ERR, "ERROR ( %s ): invalid or empty reach6_metric entry '%s'. ", filename, value);
611     return TRUE;
612   }
613   else entry->reach6_metric_num = idx;
614 
615   return FALSE;
616 }
617 
igp_daemon_map_validate(char * filename,struct plugin_requests * req)618 void igp_daemon_map_validate(char *filename, struct plugin_requests *req)
619 {
620   struct igp_map_entry *entry = (struct igp_map_entry *) req->key_value_table;
621   struct pcap_pkthdr phdr;
622 
623   if (entry) {
624     if (entry->node.family && entry->area_id && (entry->adj_metric_num || entry->reach_metric_num || entry->reach6_metric_num)) {
625       u_char isis_dgram[RECEIVE_LSP_BUFFER_SIZE+sizeof(struct chdlc_header)+sizeof(struct isis_fixed_hdr)];
626       u_char *isis_dgram_ptr = isis_dgram;
627       struct chdlc_header *chdlc_hdr;
628       struct isis_fixed_hdr *isis_hdr;
629       struct isis_link_state_hdr *lsp_hdr;
630       struct idrp_info *adj_hdr, *reach_v4_hdr, *reach_v6_hdr, *proto_supported_hdr, *area_address_hdr;
631       struct is_neigh *adj;
632       struct ipv4_reachability *reach_v4;
633       struct ipv6_reachability *reach_v6;
634       struct area_address *area_addr;
635       int rem_len = sizeof(isis_dgram), cnt, tlvs_cnt = 0, pdu_len = 0;
636 
637       memset(isis_dgram, 0, sizeof(isis_dgram));
638 
639       /* can't use DLT_RAW for IS-IS, let's use DLT_CHDLC */
640       chdlc_hdr = (struct chdlc_header *) isis_dgram_ptr;
641       chdlc_hdr->address = CHDLC_MCAST_ADDR;
642       chdlc_hdr->control = CHDLC_FIXED_CONTROL;
643       chdlc_hdr->protocol = ETHERTYPE_ISO;
644 
645       isis_dgram_ptr += sizeof(struct chdlc_header);
646       if (igp_daemon_map_handle_len(&rem_len, sizeof(struct chdlc_header), req, filename)) return;
647 
648       isis_hdr = (struct isis_fixed_hdr *) isis_dgram_ptr;
649       isis_hdr->idrp = ISO10589_ISIS;
650       isis_hdr->length = 27; /* fixed: IS-IS header + LSP header */
651       isis_hdr->version1 = TRUE;
652       isis_hdr->pdu_type = 0x14;
653       isis_hdr->version2 = TRUE;
654 
655       isis_dgram_ptr += sizeof(struct isis_fixed_hdr);
656       if (igp_daemon_map_handle_len(&rem_len, sizeof(struct isis_fixed_hdr), req, filename)) return;
657 
658       lsp_hdr = (struct isis_link_state_hdr *) isis_dgram_ptr;
659       lsp_hdr->pdu_len = 0; /* updated later */
660       if (igp_daemon_map_handle_lsp_id(lsp_hdr->lsp_id, &entry->node)) return;
661       lsp_hdr->seq_num = htonl(glob_isis_seq_num);
662       lsp_hdr->rem_lifetime = htons(-1); /* maximum lifetime possible */
663       lsp_hdr->lsp_bits = 0x03; /* IS Type = L2 */
664 
665       isis_dgram_ptr += sizeof(struct isis_link_state_hdr);
666       if (igp_daemon_map_handle_len(&rem_len, sizeof(struct isis_link_state_hdr), req, filename)) return;
667 
668       proto_supported_hdr = (struct idrp_info *) isis_dgram_ptr;
669       proto_supported_hdr->value = PROTOCOLS_SUPPORTED;
670 
671       isis_dgram_ptr += sizeof(struct idrp_info);
672       if (igp_daemon_map_handle_len(&rem_len, sizeof(struct idrp_info), req, filename)) return;
673 
674       *isis_dgram_ptr = (u_char) 0xCC;
675       isis_dgram_ptr++;
676       proto_supported_hdr->len++;
677       *isis_dgram_ptr = (u_char) 0x8E;
678       isis_dgram_ptr++;
679       proto_supported_hdr->len++;
680       if (igp_daemon_map_handle_len(&rem_len, proto_supported_hdr->len, req, filename)) return;
681       pdu_len += proto_supported_hdr->len;
682       tlvs_cnt++;
683 
684       area_address_hdr = (struct idrp_info *) isis_dgram_ptr;
685       area_address_hdr->value = AREA_ADDRESSES;
686       area_address_hdr->len = sizeof(struct area_address);
687 
688       isis_dgram_ptr += sizeof(struct idrp_info);
689       if (igp_daemon_map_handle_len(&rem_len, sizeof(struct idrp_info), req, filename)) return;
690 
691       area_addr = (struct area_address *) isis_dgram_ptr;
692       area_addr->len = 3;
693       area_addr->afi = 0x49;
694       area_addr->area_id = htons(entry->area_id);
695 
696       isis_dgram_ptr += area_address_hdr->len;
697       if (igp_daemon_map_handle_len(&rem_len, area_address_hdr->len, req, filename)) return;
698       pdu_len += area_address_hdr->len;
699       tlvs_cnt++;
700 
701       if (entry->adj_metric_num) {
702         adj_hdr = (struct idrp_info *) isis_dgram_ptr;
703         adj_hdr->value = IS_NEIGHBOURS;
704         adj_hdr->len = (11 * entry->adj_metric_num) + 1;
705 	pdu_len += adj_hdr->len;
706 
707         isis_dgram_ptr += sizeof(struct idrp_info);
708         if (igp_daemon_map_handle_len(&rem_len, sizeof(struct idrp_info), req, filename)) return;
709 
710         for (cnt = 0; cnt < entry->adj_metric_num; cnt++) {
711 	  if (!cnt) {
712 	    /* reserved space must be zero */
713 	    isis_dgram_ptr++;
714             if (igp_daemon_map_handle_len(&rem_len, 1, req, filename)) return;
715 	  }
716 
717 	  adj = (struct is_neigh *) isis_dgram_ptr;
718 	  adj->metrics.metric_default = entry->adj_metric[cnt].metric;
719 	  adj->metrics.metric_error = 0x80;
720 	  adj->metrics.metric_expense = 0x80;
721 	  adj->metrics.metric_delay = 0x80;
722 	  memcpy(adj->neigh_id, &entry->adj_metric[cnt].prefix.u.prefix4, 4);
723 
724           isis_dgram_ptr += sizeof(struct is_neigh);
725           if (igp_daemon_map_handle_len(&rem_len, sizeof(struct is_neigh), req, filename)) return;
726         }
727 
728 	tlvs_cnt++;
729       }
730 
731       if (entry->reach_metric_num) {
732         reach_v4_hdr = (struct idrp_info *) isis_dgram_ptr;
733         reach_v4_hdr->value = IPV4_INT_REACHABILITY;
734         reach_v4_hdr->len = (IPV4_REACH_LEN * entry->reach_metric_num);
735 	pdu_len += reach_v4_hdr->len;
736 
737         isis_dgram_ptr += sizeof(struct idrp_info);
738         if (igp_daemon_map_handle_len(&rem_len, sizeof(struct idrp_info), req, filename)) return;
739 
740         for (cnt = 0; cnt < entry->reach_metric_num; cnt++) {
741           reach_v4 = (struct ipv4_reachability *) isis_dgram_ptr;
742           reach_v4->metrics.metric_default = entry->reach_metric[cnt].metric;
743           reach_v4->metrics.metric_error = 0x80;
744           reach_v4->metrics.metric_expense = 0x80;
745           reach_v4->metrics.metric_delay = 0x80;
746           memcpy(&reach_v4->prefix, &entry->reach_metric[cnt].prefix.u.prefix4, 4);
747           reach_v4->mask.s_addr = htonl((entry->reach_metric[cnt].prefix.prefixlen == 32) ? 0xffffffffUL :
748 					~(0xffffffffUL >> entry->reach_metric[cnt].prefix.prefixlen));
749 
750           isis_dgram_ptr += sizeof(struct ipv4_reachability);
751           if (igp_daemon_map_handle_len(&rem_len, sizeof(struct ipv4_reachability), req, filename)) return;
752         }
753 
754 	tlvs_cnt++;
755       }
756 
757       if (entry->reach6_metric_num) {
758 	int prefix_len = 0;
759 
760         reach_v6_hdr = (struct idrp_info *) isis_dgram_ptr;
761         reach_v6_hdr->value = IPV6_REACHABILITY;
762         reach_v6_hdr->len = 0;
763 
764         isis_dgram_ptr += sizeof(struct idrp_info);
765         if (igp_daemon_map_handle_len(&rem_len, sizeof(struct idrp_info), req, filename)) return;
766 
767         for (cnt = 0; cnt < entry->reach6_metric_num; cnt++) {
768           reach_v6 = (struct ipv6_reachability *) isis_dgram_ptr;
769 	  reach_v6->metric = htonl(entry->reach6_metric[cnt].metric);
770 	  reach_v6->control_info = 0;
771 	  reach_v6->prefix_len = entry->reach6_metric[cnt].prefix.prefixlen;
772 	  prefix_len = reach_v6->prefix_len / 8;
773 	  memcpy(&reach_v6->prefix, &entry->reach6_metric[cnt].prefix.u.prefix6, prefix_len);
774 
775           reach_v6_hdr->len += 6 + prefix_len;
776           isis_dgram_ptr += 6 + prefix_len;
777           if (igp_daemon_map_handle_len(&rem_len, 6 + prefix_len, req, filename)) return;
778         }
779 
780         pdu_len += reach_v6_hdr->len;
781 
782         tlvs_cnt++;
783       }
784 
785       /* wrapping up: fix lsp length */
786       pdu_len += sizeof(struct isis_fixed_hdr)+ISIS_LSP_HDR_LEN+(ISIS_TLV_HDR_LEN*tlvs_cnt);
787       lsp_hdr->pdu_len = htons(pdu_len);
788 
789       if (config.debug && config.igp_daemon_map_msglog) {
790 	memset(&phdr, 0, sizeof(phdr));
791 	phdr.len = phdr.caplen = sizeof(isis_dgram)-rem_len;
792 	pcap_dump((u_char *) idmm_fd, &phdr, isis_dgram);
793       }
794     }
795     else {
796       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): required key missing at line %d in map '%s'. Required keys are:\n",
797 		config.name, req->line_num, filename);
798       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): node, area_id, adj_metric or reach_metric or reach6_metric.\n", config.name);
799     }
800   }
801   else Log(LOG_ERR, "ERROR ( %s ): Invalid pointer to entry. ", filename);
802 }
803 
igp_daemon_map_initialize(char * filename,struct plugin_requests * req)804 void igp_daemon_map_initialize(char *filename, struct plugin_requests *req)
805 {
806   pcap_t *p;
807 
808   if (config.debug && config.igp_daemon_map_msglog) {
809     p = pcap_open_dead(DLT_CHDLC, RECEIVE_LSP_BUFFER_SIZE+sizeof(struct chdlc_header)+sizeof(struct isis_fixed_hdr));
810 
811     if ((idmm_fd = pcap_dump_open(p, config.igp_daemon_map_msglog)) == NULL) {
812       Log(LOG_ERR, "ERROR ( %s/core/ISIS ): Can not open igp_daemon_map_msglog '%s' (%s).\n",
813                 config.name, config.igp_daemon_map_msglog, pcap_geterr(p));
814       exit_gracefully(1);
815     }
816   }
817 }
818 
igp_daemon_map_finalize(char * filename,struct plugin_requests * req)819 void igp_daemon_map_finalize(char *filename, struct plugin_requests *req)
820 {
821   if (config.debug && config.igp_daemon_map_msglog) pcap_dump_close(idmm_fd);
822 }
823 
igp_daemon_map_handle_len(int * rem_len,int len,struct plugin_requests * req,char * filename)824 int igp_daemon_map_handle_len(int *rem_len, int len, struct plugin_requests *req, char *filename)
825 {
826   *rem_len -= len;
827   if (*rem_len < 0) {
828     Log(LOG_ERR, "ERROR ( %s/core/ISIS ): Resulting LSP longer than %u. Ignoring line %d in map '%s'.\n",
829 		config.name, RECEIVE_LSP_BUFFER_SIZE, req->line_num, filename);
830     return TRUE;
831   }
832 
833   return FALSE;
834 }
835 
igp_daemon_map_handle_lsp_id(u_char * lsp_id,struct host_addr * addr)836 int igp_daemon_map_handle_lsp_id(u_char *lsp_id, struct host_addr *addr)
837 {
838   u_char sysid[ISIS_SYS_ID_LEN];
839   int idx;
840 
841   memset(sysid, 0, sizeof(sysid));
842   memcpy(sysid, &addr->address.ipv4, 4);
843 
844   for (idx = 0; idx < MAX_IGP_MAP_NODES && sysid_fragment_table[idx].valid; idx++) {
845     if (!memcmp(sysid, sysid_fragment_table[idx].sysid, ISIS_SYS_ID_LEN)) {
846       /* check if maximum segment number reached */
847       if (sysid_fragment_table[idx].frag_num == 255) {
848 	Log(LOG_WARNING, "WARN ( %s/core/ISIS ): Maximum segment number (255) reached for sysid: '%s'\n", config.name, sysid);
849 	memset(lsp_id, 0, ISIS_SYS_ID_LEN + 2);
850 
851 	return TRUE;
852       }
853       else {
854         memcpy(lsp_id, sysid_fragment_table[idx].sysid, 4);
855         memcpy(lsp_id + ISIS_SYS_ID_LEN + 1, &sysid_fragment_table[idx].frag_num, 1);
856 	sysid_fragment_table[idx].frag_num++;
857 
858 	return FALSE;
859       }
860     }
861   }
862 
863   /* sys id not found: let's insert it */
864   if (idx < MAX_IGP_MAP_NODES) {
865     memcpy(sysid_fragment_table[idx].sysid, sysid, ISIS_SYS_ID_LEN);
866     sysid_fragment_table[idx].frag_num = 0;
867     sysid_fragment_table[idx].valid = TRUE;
868 
869     memcpy(lsp_id, sysid_fragment_table[idx].sysid, 4);
870     memcpy(lsp_id + ISIS_SYS_ID_LEN + 1, &sysid_fragment_table[idx].frag_num, 1);
871     sysid_fragment_table[idx].frag_num++;
872 
873     return FALSE;
874   }
875   else {
876     Log(LOG_WARNING, "WARN ( %s/core/ISIS ): Maximum number of nodes (%u) reached in igp_daemon_map\n", config.name, MAX_IGP_MAP_NODES);
877     memset(lsp_id, 0, ISIS_SYS_ID_LEN + 2);
878 
879     return TRUE;
880   }
881 }
882