Import Upstream version 1.2.2
[quagga-debian.git] / ripngd / ripng_nexthop.c
1 /* RIPngd Zebra
2  * Copyright (C) 2002 6WIND <vincent.jardin@6wind.com>
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 /* This file is required in order to support properly the RIPng nexthop
23  * feature.
24  */
25
26 #include <zebra.h>
27
28 /* For struct udphdr. */
29 #include <netinet/udp.h>
30
31 #include "linklist.h"
32 #include "stream.h"
33 #include "log.h"
34 #include "memory.h"
35 #include "vty.h"
36 #include "if.h"
37 #include "prefix.h"
38
39 #include "ripngd/ripngd.h"
40 #include "ripngd/ripng_debug.h"
41 #include "ripngd/ripng_nexthop.h"
42
43 #define DEBUG 1
44
45 #define min(a, b) ((a) < (b) ? (a) : (b))
46
47 struct ripng_rte_data {
48   struct prefix_ipv6 *p;
49   struct ripng_info *rinfo;
50   struct ripng_aggregate *aggregate;
51 };
52
53 void _ripng_rte_del(struct ripng_rte_data *A);
54 int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B);
55
56 #define METRIC_OUT(a) \
57     ((a)->rinfo ?  (a)->rinfo->metric_out : (a)->aggregate->metric_out)
58 #define NEXTHOP_OUT_PTR(a) \
59     ((a)->rinfo ?  &((a)->rinfo->nexthop_out) : &((a)->aggregate->nexthop_out))
60 #define TAG_OUT(a) \
61     ((a)->rinfo ?  (a)->rinfo->tag_out : (a)->aggregate->tag_out)
62
63 struct list *
64 ripng_rte_new(void) {
65   struct list *rte;
66
67   rte = list_new();
68   rte->cmp = (int (*)(void *, void *)) _ripng_rte_cmp;
69   rte->del = (void (*)(void *)) _ripng_rte_del;
70
71   return rte;
72 }
73
74 void
75 ripng_rte_free(struct list *ripng_rte_list) {
76   list_delete(ripng_rte_list);
77 }
78
79 /* Delete RTE */
80 void
81 _ripng_rte_del(struct ripng_rte_data *A) {
82   XFREE(MTYPE_RIPNG_RTE_DATA, A);
83 }
84
85 /* Compare RTE:
86  *  return +  if A > B
87  *         0  if A = B
88  *         -  if A < B
89  */
90 int
91 _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B) {
92   return addr6_cmp(NEXTHOP_OUT_PTR(A), NEXTHOP_OUT_PTR(B));
93 }
94
95 /* Add routing table entry */
96 void
97 ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
98               struct ripng_info *rinfo, struct ripng_aggregate *aggregate) {
99
100   struct ripng_rte_data *data;
101
102   /* At least one should not be null */
103   assert(!rinfo || !aggregate);
104
105   data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data));
106   data->p     = p;
107   data->rinfo = rinfo;
108   data->aggregate = aggregate;
109
110   listnode_add_sort(ripng_rte_list, data);
111
112
113 /* Send the RTE with the nexthop support
114  */
115 void
116 ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
117                struct sockaddr_in6 *to) {
118
119   struct ripng_rte_data *data;
120   struct listnode *node, *nnode;
121
122   struct in6_addr last_nexthop;
123   struct in6_addr myself_nexthop;
124
125   struct stream *s;
126   int num;
127   int mtu;
128   int rtemax;
129   int ret;
130
131   /* Most of the time, there is no nexthop */
132   memset(&last_nexthop, 0, sizeof(last_nexthop));
133
134   /* Use myself_nexthop if the nexthop is not a link-local address, because
135    * we remain a right path without beeing the optimal one.
136    */
137   memset(&myself_nexthop, 0, sizeof(myself_nexthop));
138
139   /* Output stream get from ripng structre.  XXX this should be
140      interface structure. */
141   s = ripng->obuf;
142
143   /* Reset stream and RTE counter. */
144   stream_reset (s);
145   num = 0;
146
147   mtu = ifp->mtu6;
148   if (mtu < 0)
149     mtu = IFMINMTU;
150
151   rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) -
152             IPV6_HDRLEN - 
153             sizeof (struct udphdr) -
154             sizeof (struct ripng_packet) +
155             sizeof (struct rte)) / sizeof (struct rte);
156
157   for (ALL_LIST_ELEMENTS (ripng_rte_list, node, nnode, data)) {
158     /* (2.1) Next hop support */
159     if (!IPV6_ADDR_SAME(&last_nexthop, NEXTHOP_OUT_PTR(data))) {
160
161       /* A nexthop entry should be at least followed by 1 RTE */
162       if (num == (rtemax-1)) {
163         ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
164                                  to, ifp);
165
166         if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
167           ripng_packet_dump((struct ripng_packet *)STREAM_DATA (s),
168                             stream_get_endp(s), "SEND");
169         num = 0;
170         stream_reset (s);
171       }
172
173       /* Add the nexthop (2.1) */
174
175       /* If the received next hop address is not a link-local address,
176        * it should be treated as 0:0:0:0:0:0:0:0.
177        */
178       if (!IN6_IS_ADDR_LINKLOCAL(NEXTHOP_OUT_PTR(data)))
179         last_nexthop = myself_nexthop;
180       else
181         last_nexthop = *NEXTHOP_OUT_PTR(data);
182
183       num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
184     } else {
185       /* Rewrite the nexthop for each new packet */
186       if ((num == 0) && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop))
187         num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
188     }
189     num = ripng_write_rte(num, s, data->p, NULL,
190                           TAG_OUT(data), METRIC_OUT(data));
191
192     if (num == rtemax) {
193       ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
194                                to, ifp);
195
196       if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
197         ripng_packet_dump((struct ripng_packet *)STREAM_DATA (s),
198                           stream_get_endp(s), "SEND");
199       num = 0;
200       stream_reset (s);
201     }
202   }
203
204   /* If unwritten RTE exist, flush it. */
205   if (num != 0) {
206     ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s),
207                              to, ifp);
208
209     if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
210       ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s),
211                          stream_get_endp (s), "SEND");
212     stream_reset (s);
213   }
214 }