1/* RIPng offset-list
2 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * 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 Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 /* RIPng support by Vincent Jardin <vincent.jardin@6wind.com>
23  * Copyright (C) 2002 6WIND
24  */
25
26#include <zebra.h>
27
28#include "if.h"
29#include "prefix.h"
30#include "filter.h"
31#include "command.h"
32#include "linklist.h"
33#include "memory.h"
34
35#include "ripngd/ripngd.h"
36
37#define RIPNG_OFFSET_LIST_IN  0
38#define RIPNG_OFFSET_LIST_OUT 1
39#define RIPNG_OFFSET_LIST_MAX 2
40
41struct ripng_offset_list
42{
43  char *ifname;
44
45  struct
46  {
47    char *alist_name;
48    /* struct access_list *alist; */
49    int metric;
50  } direct[RIPNG_OFFSET_LIST_MAX];
51};
52
53static struct list *ripng_offset_list_master;
54
55static int
56strcmp_safe (const char *s1, const char *s2)
57{
58  if (s1 == NULL && s2 == NULL)
59    return 0;
60  if (s1 == NULL)
61    return -1;
62  if (s2 == NULL)
63    return 1;
64  return strcmp (s1, s2);
65}
66
67static struct ripng_offset_list *
68ripng_offset_list_new ()
69{
70  struct ripng_offset_list *new;
71
72  new = XCALLOC (MTYPE_RIPNG_OFFSET_LIST, sizeof (struct ripng_offset_list));
73  return new;
74}
75
76static void
77ripng_offset_list_free (struct ripng_offset_list *offset)
78{
79  XFREE (MTYPE_RIPNG_OFFSET_LIST, offset);
80}
81
82static struct ripng_offset_list *
83ripng_offset_list_lookup (const char *ifname)
84{
85  struct ripng_offset_list *offset;
86  struct listnode *node, *nnode;
87
88  for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset))
89    {
90      if (strcmp_safe (offset->ifname, ifname) == 0)
91	return offset;
92    }
93  return NULL;
94}
95
96static struct ripng_offset_list *
97ripng_offset_list_get (const char *ifname)
98{
99  struct ripng_offset_list *offset;
100
101  offset = ripng_offset_list_lookup (ifname);
102  if (offset)
103    return offset;
104
105  offset = ripng_offset_list_new ();
106  if (ifname)
107    offset->ifname = strdup (ifname);
108  listnode_add_sort (ripng_offset_list_master, offset);
109
110  return offset;
111}
112
113static int
114ripng_offset_list_set (struct vty *vty, const char *alist,
115		       const char *direct_str, const char *metric_str,
116		       const char *ifname)
117{
118  int direct;
119  int metric;
120  struct ripng_offset_list *offset;
121
122  /* Check direction. */
123  if (strncmp (direct_str, "i", 1) == 0)
124    direct = RIPNG_OFFSET_LIST_IN;
125  else if (strncmp (direct_str, "o", 1) == 0)
126    direct = RIPNG_OFFSET_LIST_OUT;
127  else
128    {
129      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
130      return CMD_WARNING;
131    }
132
133  /* Check metric. */
134  metric = atoi (metric_str);
135  if (metric < 0 || metric > 16)
136    {
137      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
138      return CMD_WARNING;
139    }
140
141  /* Get offset-list structure with interface name. */
142  offset = ripng_offset_list_get (ifname);
143
144  if (offset->direct[direct].alist_name)
145    free (offset->direct[direct].alist_name);
146  offset->direct[direct].alist_name = strdup (alist);
147  offset->direct[direct].metric = metric;
148
149  return CMD_SUCCESS;
150}
151
152static int
153ripng_offset_list_unset (struct vty *vty, const char *alist,
154			 const char *direct_str, const char *metric_str,
155			 const char *ifname)
156{
157  int direct;
158  int metric;
159  struct ripng_offset_list *offset;
160
161  /* Check direction. */
162  if (strncmp (direct_str, "i", 1) == 0)
163    direct = RIPNG_OFFSET_LIST_IN;
164  else if (strncmp (direct_str, "o", 1) == 0)
165    direct = RIPNG_OFFSET_LIST_OUT;
166  else
167    {
168      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
169      return CMD_WARNING;
170    }
171
172  /* Check metric. */
173  metric = atoi (metric_str);
174  if (metric < 0 || metric > 16)
175    {
176      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
177      return CMD_WARNING;
178    }
179
180  /* Get offset-list structure with interface name. */
181  offset = ripng_offset_list_lookup (ifname);
182
183  if (offset)
184    {
185      if (offset->direct[direct].alist_name)
186	free (offset->direct[direct].alist_name);
187      offset->direct[direct].alist_name = NULL;
188
189      if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL &&
190	  offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
191	{
192	  listnode_delete (ripng_offset_list_master, offset);
193	  if (offset->ifname)
194	    free (offset->ifname);
195	  ripng_offset_list_free (offset);
196	}
197    }
198  else
199    {
200      vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE);
201      return CMD_WARNING;
202    }
203  return CMD_SUCCESS;
204}
205
206#define OFFSET_LIST_IN_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name)
207#define OFFSET_LIST_IN_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].metric)
208
209#define OFFSET_LIST_OUT_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
210#define OFFSET_LIST_OUT_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].metric)
211
212/* If metric is modifed return 1. */
213int
214ripng_offset_list_apply_in (struct prefix_ipv6 *p, struct interface *ifp,
215			    u_char *metric)
216{
217  struct ripng_offset_list *offset;
218  struct access_list *alist;
219
220  /* Look up offset-list with interface name. */
221  offset = ripng_offset_list_lookup (ifp->name);
222  if (offset && OFFSET_LIST_IN_NAME (offset))
223    {
224      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
225
226      if (alist
227	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
228	{
229	  *metric += OFFSET_LIST_IN_METRIC (offset);
230	  return 1;
231	}
232      return 0;
233    }
234  /* Look up offset-list without interface name. */
235  offset = ripng_offset_list_lookup (NULL);
236  if (offset && OFFSET_LIST_IN_NAME (offset))
237    {
238      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
239
240      if (alist
241	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
242	{
243	  *metric += OFFSET_LIST_IN_METRIC (offset);
244	  return 1;
245	}
246      return 0;
247    }
248  return 0;
249}
250
251/* If metric is modifed return 1. */
252int
253ripng_offset_list_apply_out (struct prefix_ipv6 *p, struct interface *ifp,
254			     u_char *metric)
255{
256  struct ripng_offset_list *offset;
257  struct access_list *alist;
258
259  /* Look up offset-list with interface name. */
260  offset = ripng_offset_list_lookup (ifp->name);
261  if (offset && OFFSET_LIST_OUT_NAME (offset))
262    {
263      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
264
265      if (alist
266	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
267	{
268	  *metric += OFFSET_LIST_OUT_METRIC (offset);
269	  return 1;
270	}
271      return 0;
272    }
273
274  /* Look up offset-list without interface name. */
275  offset = ripng_offset_list_lookup (NULL);
276  if (offset && OFFSET_LIST_OUT_NAME (offset))
277    {
278      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
279
280      if (alist
281	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
282	{
283	  *metric += OFFSET_LIST_OUT_METRIC (offset);
284	  return 1;
285	}
286      return 0;
287    }
288  return 0;
289}
290
291DEFUN (ripng_offset_list,
292       ripng_offset_list_cmd,
293       "offset-list WORD (in|out) <0-16>",
294       "Modify RIPng metric\n"
295       "Access-list name\n"
296       "For incoming updates\n"
297       "For outgoing updates\n"
298       "Metric value\n")
299{
300  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], NULL);
301}
302
303DEFUN (ripng_offset_list_ifname,
304       ripng_offset_list_ifname_cmd,
305       "offset-list WORD (in|out) <0-16> IFNAME",
306       "Modify RIPng metric\n"
307       "Access-list name\n"
308       "For incoming updates\n"
309       "For outgoing updates\n"
310       "Metric value\n"
311       "Interface to match\n")
312{
313  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]);
314}
315
316DEFUN (no_ripng_offset_list,
317       no_ripng_offset_list_cmd,
318       "no offset-list WORD (in|out) <0-16>",
319       NO_STR
320       "Modify RIPng metric\n"
321       "Access-list name\n"
322       "For incoming updates\n"
323       "For outgoing updates\n"
324       "Metric value\n")
325{
326  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL);
327}
328
329DEFUN (no_ripng_offset_list_ifname,
330       no_ripng_offset_list_ifname_cmd,
331       "no offset-list WORD (in|out) <0-16> IFNAME",
332       NO_STR
333       "Modify RIPng metric\n"
334       "Access-list name\n"
335       "For incoming updates\n"
336       "For outgoing updates\n"
337       "Metric value\n"
338       "Interface to match\n")
339{
340  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]);
341}
342
343static int
344offset_list_cmp (struct ripng_offset_list *o1, struct ripng_offset_list *o2)
345{
346  return strcmp_safe (o1->ifname, o2->ifname);
347}
348
349static void
350offset_list_del (struct ripng_offset_list *offset)
351{
352  if (OFFSET_LIST_IN_NAME (offset))
353    free (OFFSET_LIST_IN_NAME (offset));
354  if (OFFSET_LIST_OUT_NAME (offset))
355    free (OFFSET_LIST_OUT_NAME (offset));
356  if (offset->ifname)
357    free (offset->ifname);
358  ripng_offset_list_free (offset);
359}
360
361void
362ripng_offset_init (void)
363{
364  ripng_offset_list_master = list_new ();
365  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
366  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
367
368  install_element (RIPNG_NODE, &ripng_offset_list_cmd);
369  install_element (RIPNG_NODE, &ripng_offset_list_ifname_cmd);
370  install_element (RIPNG_NODE, &no_ripng_offset_list_cmd);
371  install_element (RIPNG_NODE, &no_ripng_offset_list_ifname_cmd);
372}
373
374void
375ripng_offset_clean (void)
376{
377  list_delete (ripng_offset_list_master);
378
379  ripng_offset_list_master = list_new ();
380  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
381  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
382}
383
384int
385config_write_ripng_offset_list (struct vty *vty)
386{
387  struct listnode *node, *nnode;
388  struct ripng_offset_list *offset;
389
390  for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset))
391    {
392      if (! offset->ifname)
393	{
394	  if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
395	    vty_out (vty, " offset-list %s in %d%s",
396		     offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
397		     offset->direct[RIPNG_OFFSET_LIST_IN].metric,
398		     VTY_NEWLINE);
399	  if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
400	    vty_out (vty, " offset-list %s out %d%s",
401		     offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
402		     offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
403		     VTY_NEWLINE);
404	}
405      else
406	{
407	  if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
408	    vty_out (vty, " offset-list %s in %d %s%s",
409		     offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
410		     offset->direct[RIPNG_OFFSET_LIST_IN].metric,
411		     offset->ifname, VTY_NEWLINE);
412	  if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
413	    vty_out (vty, " offset-list %s out %d %s%s",
414		     offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
415		     offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
416		     offset->ifname, VTY_NEWLINE);
417	}
418    }
419
420  return 0;
421}
422