1/* Distribute list functions
2 * Copyright (C) 1998, 1999 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your
9 * option) any later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING.  If not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#include "hash.h"
25#include "if.h"
26#include "filter.h"
27#include "command.h"
28#include "distribute.h"
29#include "memory.h"
30
31/* Hash of distribute list. */
32struct hash *disthash;
33
34/* Hook functions. */
35void (*distribute_add_hook) (struct distribute *);
36void (*distribute_delete_hook) (struct distribute *);
37
38struct distribute *
39distribute_new ()
40{
41  struct distribute *new;
42
43  new = XMALLOC (MTYPE_DISTRIBUTE, sizeof (struct distribute));
44  memset (new, 0, sizeof (struct distribute));
45
46  return new;
47}
48
49/* Free distribute object. */
50void
51distribute_free (struct distribute *dist)
52{
53  if (dist->ifname)
54    free (dist->ifname);
55
56  if (dist->list[DISTRIBUTE_IN])
57    free (dist->list[DISTRIBUTE_IN]);
58  if (dist->list[DISTRIBUTE_OUT])
59    free (dist->list[DISTRIBUTE_OUT]);
60
61  if (dist->prefix[DISTRIBUTE_IN])
62    free (dist->prefix[DISTRIBUTE_IN]);
63  if (dist->prefix[DISTRIBUTE_OUT])
64    free (dist->prefix[DISTRIBUTE_OUT]);
65
66  XFREE (MTYPE_DISTRIBUTE, dist);
67}
68
69/* Lookup interface's distribute list. */
70struct distribute *
71distribute_lookup (char *ifname)
72{
73  struct distribute key;
74  struct distribute *dist;
75
76  key.ifname = ifname;
77
78  dist = hash_lookup (disthash, &key);
79
80  return dist;
81}
82
83void
84distribute_list_add_hook (void (*func) (struct distribute *))
85{
86  distribute_add_hook = func;
87}
88
89void
90distribute_list_delete_hook (void (*func) (struct distribute *))
91{
92  distribute_delete_hook = func;
93}
94
95void *
96distribute_hash_alloc (struct distribute *arg)
97{
98  struct distribute *dist;
99
100  dist = distribute_new ();
101  if (arg->ifname)
102    dist->ifname = strdup (arg->ifname);
103  else
104    dist->ifname = NULL;
105  return dist;
106}
107
108/* Make new distribute list and push into hash. */
109struct distribute *
110distribute_get (char *ifname)
111{
112  struct distribute key;
113
114  key.ifname = ifname;
115
116  return hash_get (disthash, &key, distribute_hash_alloc);
117}
118
119unsigned int
120distribute_hash_make (struct distribute *dist)
121{
122  unsigned int key;
123  int i;
124
125  key = 0;
126  if (dist->ifname)
127    for (i = 0; i < strlen (dist->ifname); i++)
128      key += dist->ifname[i];
129
130  return key;
131}
132
133/* If two distribute-list have same value then return 1 else return
134   0. This function is used by hash package. */
135int
136distribute_cmp (struct distribute *dist1, struct distribute *dist2)
137{
138  if (dist1->ifname && dist2->ifname)
139    if (strcmp (dist1->ifname, dist2->ifname) == 0)
140      return 1;
141  if (! dist1->ifname && ! dist2->ifname)
142    return 1;
143  return 0;
144}
145
146/* Set access-list name to the distribute list. */
147struct distribute *
148distribute_list_set (char *ifname, enum distribute_type type, char *alist_name)
149{
150  struct distribute *dist;
151
152  dist = distribute_get (ifname);
153
154  if (type == DISTRIBUTE_IN)
155    {
156      if (dist->list[DISTRIBUTE_IN])
157	free (dist->list[DISTRIBUTE_IN]);
158      dist->list[DISTRIBUTE_IN] = strdup (alist_name);
159    }
160  if (type == DISTRIBUTE_OUT)
161    {
162      if (dist->list[DISTRIBUTE_OUT])
163	free (dist->list[DISTRIBUTE_OUT]);
164      dist->list[DISTRIBUTE_OUT] = strdup (alist_name);
165    }
166
167  /* Apply this distribute-list to the interface. */
168  (*distribute_add_hook) (dist);
169
170  return dist;
171}
172
173/* Unset distribute-list.  If matched distribute-list exist then
174   return 1. */
175int
176distribute_list_unset (char *ifname, enum distribute_type type,
177		       char *alist_name)
178{
179  struct distribute *dist;
180
181  dist = distribute_lookup (ifname);
182  if (!dist)
183    return 0;
184
185  if (type == DISTRIBUTE_IN)
186    {
187      if (!dist->list[DISTRIBUTE_IN])
188	return 0;
189      if (strcmp (dist->list[DISTRIBUTE_IN], alist_name) != 0)
190	return 0;
191
192      free (dist->list[DISTRIBUTE_IN]);
193      dist->list[DISTRIBUTE_IN] = NULL;
194    }
195
196  if (type == DISTRIBUTE_OUT)
197    {
198      if (!dist->list[DISTRIBUTE_OUT])
199	return 0;
200      if (strcmp (dist->list[DISTRIBUTE_OUT], alist_name) != 0)
201	return 0;
202
203      free (dist->list[DISTRIBUTE_OUT]);
204      dist->list[DISTRIBUTE_OUT] = NULL;
205    }
206
207  /* Apply this distribute-list to the interface. */
208  (*distribute_delete_hook) (dist);
209
210  /* If both out and in is NULL then free distribute list. */
211  if (dist->list[DISTRIBUTE_IN] == NULL &&
212      dist->list[DISTRIBUTE_OUT] == NULL &&
213      dist->prefix[DISTRIBUTE_IN] == NULL &&
214      dist->prefix[DISTRIBUTE_OUT] == NULL)
215    {
216      hash_release (disthash, dist);
217      distribute_free (dist);
218    }
219
220  return 1;
221}
222
223#ifdef FOX_CMD_SUPPORT
224/* Set access-list name to the distribute list. */
225struct distribute *
226distribute_list_prefix_set (char *ifname, enum distribute_type type,
227			    char *plist_name)
228{
229  struct distribute *dist;
230
231  dist = distribute_get (ifname);
232
233  if (type == DISTRIBUTE_IN)
234    {
235      if (dist->prefix[DISTRIBUTE_IN])
236	free (dist->prefix[DISTRIBUTE_IN]);
237      dist->prefix[DISTRIBUTE_IN] = strdup (plist_name);
238    }
239  if (type == DISTRIBUTE_OUT)
240    {
241      if (dist->prefix[DISTRIBUTE_OUT])
242	free (dist->prefix[DISTRIBUTE_OUT]);
243      dist->prefix[DISTRIBUTE_OUT] = strdup (plist_name);
244    }
245
246  /* Apply this distribute-list to the interface. */
247  (*distribute_add_hook) (dist);
248
249  return dist;
250}
251
252/* Unset distribute-list.  If matched distribute-list exist then
253   return 1. */
254int
255distribute_list_prefix_unset (char *ifname, enum distribute_type type,
256			      char *plist_name)
257{
258  struct distribute *dist;
259
260  dist = distribute_lookup (ifname);
261  if (!dist)
262    return 0;
263
264  if (type == DISTRIBUTE_IN)
265    {
266      if (!dist->prefix[DISTRIBUTE_IN])
267	return 0;
268      if (strcmp (dist->prefix[DISTRIBUTE_IN], plist_name) != 0)
269	return 0;
270
271      free (dist->prefix[DISTRIBUTE_IN]);
272      dist->prefix[DISTRIBUTE_IN] = NULL;
273    }
274
275  if (type == DISTRIBUTE_OUT)
276    {
277      if (!dist->prefix[DISTRIBUTE_OUT])
278	return 0;
279      if (strcmp (dist->prefix[DISTRIBUTE_OUT], plist_name) != 0)
280	return 0;
281
282      free (dist->prefix[DISTRIBUTE_OUT]);
283      dist->prefix[DISTRIBUTE_OUT] = NULL;
284    }
285
286  /* Apply this distribute-list to the interface. */
287  (*distribute_delete_hook) (dist);
288
289  /* If both out and in is NULL then free distribute list. */
290  if (dist->list[DISTRIBUTE_IN] == NULL &&
291      dist->list[DISTRIBUTE_OUT] == NULL &&
292      dist->prefix[DISTRIBUTE_IN] == NULL &&
293      dist->prefix[DISTRIBUTE_OUT] == NULL)
294    {
295      hash_release (disthash, dist);
296      distribute_free (dist);
297    }
298
299  return 1;
300}
301
302DEFUN (distribute_list_all,
303       distribute_list_all_cmd,
304       "distribute-list WORD (in|out)",
305       "Filter networks in routing updates\n"
306       "Access-list name\n"
307       "Filter incoming routing updates\n"
308       "Filter outgoing routing updates\n")
309{
310  enum distribute_type type;
311  struct distribute *dist;
312
313  /* Check of distribute list type. */
314  if (strncmp (argv[1], "i", 1) == 0)
315    type = DISTRIBUTE_IN;
316  else if (strncmp (argv[1], "o", 1) == 0)
317    type = DISTRIBUTE_OUT;
318  else
319    {
320      vty_out (vty, "distribute list direction must be [in|out]%s",
321	       VTY_NEWLINE);
322      return CMD_WARNING;
323    }
324
325  /* Get interface name corresponding distribute list. */
326  dist = distribute_list_set (NULL, type, argv[0]);
327
328  return CMD_SUCCESS;
329}
330
331DEFUN (no_distribute_list_all,
332       no_distribute_list_all_cmd,
333       "no distribute-list WORD (in|out)",
334       NO_STR
335       "Filter networks in routing updates\n"
336       "Access-list name\n"
337       "Filter incoming routing updates\n"
338       "Filter outgoing routing updates\n")
339{
340  int ret;
341  enum distribute_type type;
342
343  /* Check of distribute list type. */
344  if (strncmp (argv[1], "i", 1) == 0)
345    type = DISTRIBUTE_IN;
346  else if (strncmp (argv[1], "o", 1) == 0)
347    type = DISTRIBUTE_OUT;
348  else
349    {
350      vty_out (vty, "distribute list direction must be [in|out]%s",
351	       VTY_NEWLINE);
352      return CMD_WARNING;
353    }
354
355  ret = distribute_list_unset (NULL, type, argv[0]);
356  if (! ret)
357    {
358      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
359      return CMD_WARNING;
360    }
361  return CMD_SUCCESS;
362}
363#endif /* FOX_CMD_SUPPORT */
364
365DEFUN (distribute_list,
366       distribute_list_cmd,
367       "distribute-list WORD (in|out) WORD",
368       "Filter networks in routing updates\n"
369       "Access-list name\n"
370       "Filter incoming routing updates\n"
371       "Filter outgoing routing updates\n"
372       "Interface name\n")
373{
374  enum distribute_type type;
375  struct distribute *dist;
376
377  /* Check of distribute list type. */
378  if (strncmp (argv[1], "i", 1) == 0)
379    type = DISTRIBUTE_IN;
380  else if (strncmp (argv[1], "o", 1) == 0)
381    type = DISTRIBUTE_OUT;
382  else
383    {
384      vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
385      return CMD_WARNING;
386    }
387
388  /* Get interface name corresponding distribute list. */
389  dist = distribute_list_set (argv[2], type, argv[0]);
390
391  return CMD_SUCCESS;
392}
393
394DEFUN (no_districute_list, no_distribute_list_cmd,
395       "no distribute-list WORD (in|out) WORD",
396       NO_STR
397       "Filter networks in routing updates\n"
398       "Access-list name\n"
399       "Filter incoming routing updates\n"
400       "Filter outgoing routing updates\n"
401       "Interface name\n")
402{
403  int ret;
404  enum distribute_type type;
405
406  /* Check of distribute list type. */
407  if (strncmp (argv[1], "i", 1) == 0)
408    type = DISTRIBUTE_IN;
409  else if (strncmp (argv[1], "o", 1) == 0)
410    type = DISTRIBUTE_OUT;
411  else
412    {
413      vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE);
414      return CMD_WARNING;
415    }
416
417  ret = distribute_list_unset (argv[2], type, argv[0]);
418  if (! ret)
419    {
420      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
421      return CMD_WARNING;
422    }
423  return CMD_SUCCESS;
424}
425
426#ifdef FOX_CMD_SUPPORT
427DEFUN (districute_list_prefix_all,
428       distribute_list_prefix_all_cmd,
429       "distribute-list prefix WORD (in|out)",
430       "Filter networks in routing updates\n"
431       "Filter prefixes in routing updates\n"
432       "Name of an IP prefix-list\n"
433       "Filter incoming routing updates\n"
434       "Filter outgoing routing updates\n")
435{
436  enum distribute_type type;
437  struct distribute *dist;
438
439  /* Check of distribute list type. */
440  if (strncmp (argv[1], "i", 1) == 0)
441    type = DISTRIBUTE_IN;
442  else if (strncmp (argv[1], "o", 1) == 0)
443    type = DISTRIBUTE_OUT;
444  else
445    {
446      vty_out (vty, "distribute list direction must be [in|out]%s",
447	       VTY_NEWLINE);
448      return CMD_WARNING;
449    }
450
451  /* Get interface name corresponding distribute list. */
452  dist = distribute_list_prefix_set (NULL, type, argv[0]);
453
454  return CMD_SUCCESS;
455}
456
457DEFUN (no_districute_list_prefix_all,
458       no_distribute_list_prefix_all_cmd,
459       "no distribute-list prefix WORD (in|out)",
460       NO_STR
461       "Filter networks in routing updates\n"
462       "Filter prefixes in routing updates\n"
463       "Name of an IP prefix-list\n"
464       "Filter incoming routing updates\n"
465       "Filter outgoing routing updates\n")
466{
467  int ret;
468  enum distribute_type type;
469
470  /* Check of distribute list type. */
471  if (strncmp (argv[1], "i", 1) == 0)
472    type = DISTRIBUTE_IN;
473  else if (strncmp (argv[1], "o", 1) == 0)
474    type = DISTRIBUTE_OUT;
475  else
476    {
477      vty_out (vty, "distribute list direction must be [in|out]%s",
478	       VTY_NEWLINE);
479      return CMD_WARNING;
480    }
481
482  ret = distribute_list_prefix_unset (NULL, type, argv[0]);
483  if (! ret)
484    {
485      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
486      return CMD_WARNING;
487    }
488  return CMD_SUCCESS;
489}
490
491DEFUN (districute_list_prefix, distribute_list_prefix_cmd,
492       "distribute-list prefix WORD (in|out) WORD",
493       "Filter networks in routing updates\n"
494       "Filter prefixes in routing updates\n"
495       "Name of an IP prefix-list\n"
496       "Filter incoming routing updates\n"
497       "Filter outgoing routing updates\n"
498       "Interface name\n")
499{
500  enum distribute_type type;
501  struct distribute *dist;
502
503  /* Check of distribute list type. */
504  if (strncmp (argv[1], "i", 1) == 0)
505    type = DISTRIBUTE_IN;
506  else if (strncmp (argv[1], "o", 1) == 0)
507    type = DISTRIBUTE_OUT;
508  else
509    {
510      vty_out (vty, "distribute list direction must be [in|out]%s",
511	       VTY_NEWLINE);
512      return CMD_WARNING;
513    }
514
515  /* Get interface name corresponding distribute list. */
516  dist = distribute_list_prefix_set (argv[2], type, argv[0]);
517
518  return CMD_SUCCESS;
519}
520
521DEFUN (no_districute_list_prefix, no_distribute_list_prefix_cmd,
522       "no distribute-list prefix WORD (in|out) WORD",
523       NO_STR
524       "Filter networks in routing updates\n"
525       "Filter prefixes in routing updates\n"
526       "Name of an IP prefix-list\n"
527       "Filter incoming routing updates\n"
528       "Filter outgoing routing updates\n"
529       "Interface name\n")
530{
531  int ret;
532  enum distribute_type type;
533
534  /* Check of distribute list type. */
535  if (strncmp (argv[1], "i", 1) == 0)
536    type = DISTRIBUTE_IN;
537  else if (strncmp (argv[1], "o", 1) == 0)
538    type = DISTRIBUTE_OUT;
539  else
540    {
541      vty_out (vty, "distribute list direction must be [in|out]%s",
542	       VTY_NEWLINE);
543      return CMD_WARNING;
544    }
545
546  ret = distribute_list_prefix_unset (argv[2], type, argv[0]);
547  if (! ret)
548    {
549      vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE);
550      return CMD_WARNING;
551    }
552  return CMD_SUCCESS;
553}
554#endif /* FOX_CMD_SUPPORT */
555
556int
557config_show_distribute (struct vty *vty)
558{
559  int i;
560  struct hash_backet *mp;
561  struct distribute *dist;
562
563  /* Output filter configuration. */
564  dist = distribute_lookup (NULL);
565  if (dist && (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT]))
566    {
567      vty_out (vty, "  Outgoing update filter list for all interface is");
568      if (dist->list[DISTRIBUTE_OUT])
569	vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]);
570      if (dist->prefix[DISTRIBUTE_OUT])
571	vty_out (vty, "%s (prefix-list) %s",
572		 dist->list[DISTRIBUTE_OUT] ? "," : "",
573		 dist->prefix[DISTRIBUTE_OUT]);
574      vty_out (vty, "%s", VTY_NEWLINE);
575    }
576  else
577    vty_out (vty, "  Outgoing update filter list for all interface is not set%s", VTY_NEWLINE);
578
579  for (i = 0; i < disthash->size; i++)
580    for (mp = disthash->index[i]; mp; mp = mp->next)
581      {
582	dist = mp->data;
583	if (dist->ifname)
584	  if (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT])
585	    {
586	      vty_out (vty, "    %s filtered by", dist->ifname);
587	      if (dist->list[DISTRIBUTE_OUT])
588		vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]);
589	      if (dist->prefix[DISTRIBUTE_OUT])
590		vty_out (vty, "%s (prefix-list) %s",
591			 dist->list[DISTRIBUTE_OUT] ? "," : "",
592			 dist->prefix[DISTRIBUTE_OUT]);
593	      vty_out (vty, "%s", VTY_NEWLINE);
594	    }
595      }
596
597
598  /* Input filter configuration. */
599  dist = distribute_lookup (NULL);
600  if (dist && (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN]))
601    {
602      vty_out (vty, "  Incoming update filter list for all interface is");
603      if (dist->list[DISTRIBUTE_IN])
604	vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]);
605      if (dist->prefix[DISTRIBUTE_IN])
606	vty_out (vty, "%s (prefix-list) %s",
607		 dist->list[DISTRIBUTE_IN] ? "," : "",
608		 dist->prefix[DISTRIBUTE_IN]);
609      vty_out (vty, "%s", VTY_NEWLINE);
610    }
611  else
612    vty_out (vty, "  Incoming update filter list for all interface is not set%s", VTY_NEWLINE);
613
614  for (i = 0; i < disthash->size; i++)
615    for (mp = disthash->index[i]; mp; mp = mp->next)
616      {
617	dist = mp->data;
618	if (dist->ifname)
619	  if (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN])
620	    {
621	      vty_out (vty, "    %s filtered by", dist->ifname);
622	      if (dist->list[DISTRIBUTE_IN])
623		vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]);
624	      if (dist->prefix[DISTRIBUTE_IN])
625		vty_out (vty, "%s (prefix-list) %s",
626			 dist->list[DISTRIBUTE_IN] ? "," : "",
627			 dist->prefix[DISTRIBUTE_IN]);
628	      vty_out (vty, "%s", VTY_NEWLINE);
629	    }
630      }
631  return 0;
632}
633
634/* Configuration write function. */
635int
636config_write_distribute (struct vty *vty)
637{
638  int i;
639  struct hash_backet *mp;
640  int write = 0;
641
642  for (i = 0; i < disthash->size; i++)
643    for (mp = disthash->index[i]; mp; mp = mp->next)
644      {
645	struct distribute *dist;
646
647	dist = mp->data;
648
649	if (dist->list[DISTRIBUTE_IN])
650	  {
651	    vty_out (vty, " distribute-list %s in %s%s",
652		     dist->list[DISTRIBUTE_IN],
653		     dist->ifname ? dist->ifname : "",
654		     VTY_NEWLINE);
655	    write++;
656	  }
657
658	if (dist->list[DISTRIBUTE_OUT])
659	  {
660	    vty_out (vty, " distribute-list %s out %s%s",
661
662		     dist->list[DISTRIBUTE_OUT],
663		     dist->ifname ? dist->ifname : "",
664		     VTY_NEWLINE);
665	    write++;
666	  }
667
668	if (dist->prefix[DISTRIBUTE_IN])
669	  {
670	    vty_out (vty, " distribute-list prefix %s in %s%s",
671		     dist->prefix[DISTRIBUTE_IN],
672		     dist->ifname ? dist->ifname : "",
673		     VTY_NEWLINE);
674	    write++;
675	  }
676
677	if (dist->prefix[DISTRIBUTE_OUT])
678	  {
679	    vty_out (vty, " distribute-list prefix %s out %s%s",
680		     dist->prefix[DISTRIBUTE_OUT],
681		     dist->ifname ? dist->ifname : "",
682		     VTY_NEWLINE);
683	    write++;
684	  }
685      }
686  return write;
687}
688
689/* Clear all distribute list. */
690void
691distribute_list_reset ()
692{
693  hash_clean (disthash, (void (*) (void *)) distribute_free);
694}
695
696/* Initialize distribute list related hash. */
697void
698distribute_list_init (int node)
699{
700  disthash = hash_create (distribute_hash_make, distribute_cmp);
701
702#ifdef FOX_CMD_SUPPORT
703  install_element (node, &distribute_list_all_cmd);
704  install_element (node, &no_distribute_list_all_cmd);
705#endif /* FOX_CMD_SUPPORT */
706
707  install_element (node, &distribute_list_cmd);
708  install_element (node, &no_distribute_list_cmd);
709#ifdef FOX_CMD_SUPPORT
710  install_element (node, &distribute_list_prefix_all_cmd);
711  install_element (node, &no_distribute_list_prefix_all_cmd);
712
713  install_element (node, &distribute_list_prefix_cmd);
714  install_element (node, &no_distribute_list_prefix_cmd);
715#endif /* FOX_CMD_SUPPORT */
716}
717