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 
41 struct 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 
53 static struct list *ripng_offset_list_master;
54 
55 static int
strcmp_safe(const char * s1,const char * s2)56 strcmp_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 
67 static struct ripng_offset_list *
ripng_offset_list_new()68 ripng_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 
76 static void
ripng_offset_list_free(struct ripng_offset_list * offset)77 ripng_offset_list_free (struct ripng_offset_list *offset)
78 {
79   XFREE (MTYPE_RIPNG_OFFSET_LIST, offset);
80 }
81 
82 static struct ripng_offset_list *
ripng_offset_list_lookup(const char * ifname)83 ripng_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 
96 static struct ripng_offset_list *
ripng_offset_list_get(const char * ifname)97 ripng_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 
113 static int
ripng_offset_list_set(struct vty * vty,const char * alist,const char * direct_str,const char * metric_str,const char * ifname)114 ripng_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 
152 static int
ripng_offset_list_unset(struct vty * vty,const char * alist,const char * direct_str,const char * metric_str,const char * ifname)153 ripng_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. */
213 int
ripng_offset_list_apply_in(struct prefix_ipv6 * p,struct interface * ifp,u_char * metric)214 ripng_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. */
252 int
ripng_offset_list_apply_out(struct prefix_ipv6 * p,struct interface * ifp,u_char * metric)253 ripng_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 
291 DEFUN (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 
303 DEFUN (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 
316 DEFUN (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 
329 DEFUN (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 
343 static int
offset_list_cmp(struct ripng_offset_list * o1,struct ripng_offset_list * o2)344 offset_list_cmp (struct ripng_offset_list *o1, struct ripng_offset_list *o2)
345 {
346   return strcmp_safe (o1->ifname, o2->ifname);
347 }
348 
349 static void
offset_list_del(struct ripng_offset_list * offset)350 offset_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 
361 void
ripng_offset_init(void)362 ripng_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 
374 void
ripng_offset_clean(void)375 ripng_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 
384 int
config_write_ripng_offset_list(struct vty * vty)385 config_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