New upstream release and new maintainer
[quagga-debian.git] / zebra / zebra_fpm_protobuf.c
1 /*
2  * zebra_fpm_protobuf.c
3  *
4  * @copyright Copyright (C) 2016 Sproute Networks, Inc.
5  *
6  * @author Avneesh Sachdev <avneesh@sproute.com>
7  *
8  * This file is part of Quagga.
9  *
10  * Quagga is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2, or (at your option) any
13  * later version.
14  *
15  * Quagga is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Quagga; see the file COPYING.  If not, write to the Free
22  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23  * 02111-1307, USA.
24  */
25 #include <zebra.h>
26
27 #include "log.h"
28 #include "rib.h"
29
30 #include "qpb/qpb.pb-c.h"
31 #include "qpb/qpb.h"
32 #include "qpb/qpb_allocator.h"
33 #include "qpb/linear_allocator.h"
34 #include "fpm/fpm_pb.h"
35
36 #include "zebra_fpm_private.h"
37
38 /*
39  * create_delete_route_message
40  */
41 static Fpm__DeleteRoute *
42 create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
43                              struct rib *rib)
44 {
45   Fpm__DeleteRoute *msg;
46
47   msg = QPB_ALLOC(allocator, typeof(*msg));
48   if (!msg) {
49     assert(0);
50     return NULL;
51   }
52
53   fpm__delete_route__init(msg);
54   msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
55
56   qpb_address_family_set(&msg->address_family, rib_dest_af(dest));
57
58   /*
59    * XXX Hardcode subaddress family for now.
60    */
61   msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
62   msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
63   if (!msg->key) {
64     assert(0);
65     return NULL;
66   }
67
68   return msg;
69 }
70
71 /*
72  * add_nexthop
73  */
74 static inline int
75 add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest,
76              struct nexthop *nexthop)
77 {
78   uint32_t if_index;
79   union g_addr *gateway, *src;
80
81   gateway = src = NULL;
82
83   if_index = nexthop->ifindex;
84
85   if (nexthop->type == NEXTHOP_TYPE_IPV4
86       || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
87     {
88       gateway = &nexthop->gate;
89       if (nexthop->src.ipv4.s_addr)
90         src = &nexthop->src;
91     }
92
93   if (nexthop->type == NEXTHOP_TYPE_IPV6
94       || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
95       || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
96     {
97       gateway = &nexthop->gate;
98     }
99
100   if (nexthop->type == NEXTHOP_TYPE_IFINDEX
101       || nexthop->type == NEXTHOP_TYPE_IFNAME)
102     {
103       if (nexthop->src.ipv4.s_addr)
104         src = &nexthop->src;
105     }
106
107   if (!gateway && if_index == 0)
108     return 0;
109
110   /*
111    * We have a valid nexthop.
112    */
113   {
114     Fpm__Nexthop *pb_nh;
115     pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh));
116     if (!pb_nh) {
117       assert(0);
118       return 0;
119     }
120
121     fpm__nexthop__init(pb_nh);
122
123     if (if_index != 0) {
124       pb_nh->if_id = qpb_if_identifier_create (allocator, if_index);
125     }
126
127     if (gateway) {
128       pb_nh->address = qpb_l3_address_create (allocator, gateway,
129                                               rib_dest_af(dest));
130     }
131
132     msg->nexthops[msg->n_nexthops++] = pb_nh;
133   }
134
135   // TODO: Use src.
136
137   return 1;
138 }
139
140 /*
141  * create_add_route_message
142  */
143 static Fpm__AddRoute *
144 create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
145                           struct rib *rib)
146 {
147   Fpm__AddRoute *msg;
148   int discard;
149   struct nexthop *nexthop, *tnexthop;
150   int recursing;
151   uint num_nhs, u;
152   struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)];
153
154   msg = QPB_ALLOC(allocator, typeof(*msg));
155   if (!msg) {
156     assert(0);
157     return NULL;
158   }
159
160   fpm__add_route__init(msg);
161
162   msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
163
164   qpb_address_family_set (&msg->address_family, rib_dest_af(dest));
165
166   /*
167    * XXX Hardcode subaddress family for now.
168    */
169   msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
170   msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
171   qpb_protocol_set (&msg->protocol, rib->type);
172
173   if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
174     discard = 1;
175   else
176     discard = 0;
177
178   if (discard)
179     {
180       if (rib->flags & ZEBRA_FLAG_BLACKHOLE) {
181         msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE;
182       } else if (rib->flags & ZEBRA_FLAG_REJECT) {
183         msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE;
184       } else {
185         assert (0);
186       }
187       return msg;
188     }
189   else {
190     msg->route_type = FPM__ROUTE_TYPE__NORMAL;
191   }
192
193   msg->metric = rib->metric;
194
195   /*
196    * Figure out the set of nexthops to be added to the message.
197    */
198   num_nhs = 0;
199   for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing))
200     {
201       if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM)
202         break;
203
204       if (num_nhs >= ZEBRA_NUM_OF(nexthops))
205         break;
206
207       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
208         continue;
209
210       if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
211         continue;
212
213       nexthops[num_nhs] = nexthop;
214       num_nhs++;
215     }
216
217   if (!num_nhs) {
218     zfpm_debug ("netlink_encode_route(): No useful nexthop.");
219     assert(0);
220     return NULL;
221   }
222
223   /*
224    * And add them to the message.
225    */
226   if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) {
227     assert(0);
228     return NULL;
229   }
230
231   msg->n_nexthops = 0;
232   for (u = 0; u < num_nhs; u++) {
233     if (!add_nexthop(allocator, msg, dest, nexthops[u])) {
234       assert(0);
235       return NULL;
236     }
237   }
238
239   assert(msg->n_nexthops == num_nhs);
240
241   return msg;
242 }
243
244 /*
245  * create_route_message
246  */
247 static Fpm__Message *
248 create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
249                       struct rib *rib)
250 {
251   Fpm__Message *msg;
252
253   msg = QPB_ALLOC(allocator, typeof(*msg));
254   if (!msg) {
255     assert(0);
256     return NULL;
257   }
258
259   fpm__message__init(msg);
260
261   if (!rib) {
262     msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE;
263     msg->delete_route = create_delete_route_message(allocator, dest, rib);
264     if (!msg->delete_route) {
265       assert(0);
266       return NULL;
267     }
268     return msg;
269   }
270
271   msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE;
272   msg->add_route = create_add_route_message(allocator, dest, rib);
273   if (!msg->add_route) {
274     assert(0);
275     return NULL;
276   }
277
278   return msg;
279 }
280
281 /*
282  * zfpm_protobuf_encode_route
283  *
284  * Create a protobuf message corresponding to the given route in the
285  * given buffer space.
286  *
287  * Returns the number of bytes written to the buffer. 0 or a negative
288  * value indicates an error.
289  */
290 int
291 zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib,
292                             uint8_t *in_buf, size_t in_buf_len)
293 {
294   Fpm__Message *msg;
295   QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096);
296   size_t len;
297
298   QPB_INIT_STACK_ALLOCATOR (allocator);
299
300   msg = create_route_message(&allocator, dest, rib);
301   if (!msg) {
302     assert(0);
303     return 0;
304   }
305
306   len = fpm__message__pack(msg, (uint8_t *) in_buf);
307   assert(len <= in_buf_len);
308
309   QPB_RESET_STACK_ALLOCATOR (allocator);
310   return len;
311 }