Import Upstream version 1.2.2
[quagga-debian.git] / ripngd / ripng_offset.c
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
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 *
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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 }