| /* |
| * zebra_fpm_protobuf.c |
| * |
| * @copyright Copyright (C) 2016 Sproute Networks, Inc. |
| * |
| * @author Avneesh Sachdev <avneesh@sproute.com> |
| * |
| * This file is part of Quagga. |
| * |
| * Quagga is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * Quagga is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Quagga; see the file COPYING. If not, write to the Free |
| * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| #include <zebra.h> |
| |
| #include "log.h" |
| #include "rib.h" |
| |
| #include "qpb/qpb.pb-c.h" |
| #include "qpb/qpb.h" |
| #include "qpb/qpb_allocator.h" |
| #include "qpb/linear_allocator.h" |
| #include "fpm/fpm_pb.h" |
| |
| #include "zebra_fpm_private.h" |
| |
| /* |
| * create_delete_route_message |
| */ |
| static Fpm__DeleteRoute * |
| create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, |
| struct rib *rib) |
| { |
| Fpm__DeleteRoute *msg; |
| |
| msg = QPB_ALLOC(allocator, typeof(*msg)); |
| if (!msg) { |
| assert(0); |
| return NULL; |
| } |
| |
| fpm__delete_route__init(msg); |
| msg->vrf_id = rib_dest_vrf(dest)->vrf_id; |
| |
| qpb_address_family_set(&msg->address_family, rib_dest_af(dest)); |
| |
| /* |
| * XXX Hardcode subaddress family for now. |
| */ |
| msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; |
| msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); |
| if (!msg->key) { |
| assert(0); |
| return NULL; |
| } |
| |
| return msg; |
| } |
| |
| /* |
| * add_nexthop |
| */ |
| static inline int |
| add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest, |
| struct nexthop *nexthop) |
| { |
| uint32_t if_index; |
| union g_addr *gateway, *src; |
| |
| gateway = src = NULL; |
| |
| if_index = nexthop->ifindex; |
| |
| if (nexthop->type == NEXTHOP_TYPE_IPV4 |
| || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| gateway = &nexthop->gate; |
| if (nexthop->src.ipv4.s_addr) |
| src = &nexthop->src; |
| } |
| |
| if (nexthop->type == NEXTHOP_TYPE_IPV6 |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) |
| { |
| gateway = &nexthop->gate; |
| } |
| |
| if (nexthop->type == NEXTHOP_TYPE_IFINDEX |
| || nexthop->type == NEXTHOP_TYPE_IFNAME) |
| { |
| if (nexthop->src.ipv4.s_addr) |
| src = &nexthop->src; |
| } |
| |
| if (!gateway && if_index == 0) |
| return 0; |
| |
| /* |
| * We have a valid nexthop. |
| */ |
| { |
| Fpm__Nexthop *pb_nh; |
| pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh)); |
| if (!pb_nh) { |
| assert(0); |
| return 0; |
| } |
| |
| fpm__nexthop__init(pb_nh); |
| |
| if (if_index != 0) { |
| pb_nh->if_id = qpb_if_identifier_create (allocator, if_index); |
| } |
| |
| if (gateway) { |
| pb_nh->address = qpb_l3_address_create (allocator, gateway, |
| rib_dest_af(dest)); |
| } |
| |
| msg->nexthops[msg->n_nexthops++] = pb_nh; |
| } |
| |
| // TODO: Use src. |
| |
| return 1; |
| } |
| |
| /* |
| * create_add_route_message |
| */ |
| static Fpm__AddRoute * |
| create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, |
| struct rib *rib) |
| { |
| Fpm__AddRoute *msg; |
| int discard; |
| struct nexthop *nexthop, *tnexthop; |
| int recursing; |
| uint num_nhs, u; |
| struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)]; |
| |
| msg = QPB_ALLOC(allocator, typeof(*msg)); |
| if (!msg) { |
| assert(0); |
| return NULL; |
| } |
| |
| fpm__add_route__init(msg); |
| |
| msg->vrf_id = rib_dest_vrf(dest)->vrf_id; |
| |
| qpb_address_family_set (&msg->address_family, rib_dest_af(dest)); |
| |
| /* |
| * XXX Hardcode subaddress family for now. |
| */ |
| msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; |
| msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); |
| qpb_protocol_set (&msg->protocol, rib->type); |
| |
| if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) |
| discard = 1; |
| else |
| discard = 0; |
| |
| if (discard) |
| { |
| if (rib->flags & ZEBRA_FLAG_BLACKHOLE) { |
| msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE; |
| } else if (rib->flags & ZEBRA_FLAG_REJECT) { |
| msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE; |
| } else { |
| assert (0); |
| } |
| return msg; |
| } |
| else { |
| msg->route_type = FPM__ROUTE_TYPE__NORMAL; |
| } |
| |
| msg->metric = rib->metric; |
| |
| /* |
| * Figure out the set of nexthops to be added to the message. |
| */ |
| num_nhs = 0; |
| for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing)) |
| { |
| if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM) |
| break; |
| |
| if (num_nhs >= ZEBRA_NUM_OF(nexthops)) |
| break; |
| |
| if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
| continue; |
| |
| if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| continue; |
| |
| nexthops[num_nhs] = nexthop; |
| num_nhs++; |
| } |
| |
| if (!num_nhs) { |
| zfpm_debug ("netlink_encode_route(): No useful nexthop."); |
| assert(0); |
| return NULL; |
| } |
| |
| /* |
| * And add them to the message. |
| */ |
| if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) { |
| assert(0); |
| return NULL; |
| } |
| |
| msg->n_nexthops = 0; |
| for (u = 0; u < num_nhs; u++) { |
| if (!add_nexthop(allocator, msg, dest, nexthops[u])) { |
| assert(0); |
| return NULL; |
| } |
| } |
| |
| assert(msg->n_nexthops == num_nhs); |
| |
| return msg; |
| } |
| |
| /* |
| * create_route_message |
| */ |
| static Fpm__Message * |
| create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, |
| struct rib *rib) |
| { |
| Fpm__Message *msg; |
| |
| msg = QPB_ALLOC(allocator, typeof(*msg)); |
| if (!msg) { |
| assert(0); |
| return NULL; |
| } |
| |
| fpm__message__init(msg); |
| |
| if (!rib) { |
| msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE; |
| msg->delete_route = create_delete_route_message(allocator, dest, rib); |
| if (!msg->delete_route) { |
| assert(0); |
| return NULL; |
| } |
| return msg; |
| } |
| |
| msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE; |
| msg->add_route = create_add_route_message(allocator, dest, rib); |
| if (!msg->add_route) { |
| assert(0); |
| return NULL; |
| } |
| |
| return msg; |
| } |
| |
| /* |
| * zfpm_protobuf_encode_route |
| * |
| * Create a protobuf message corresponding to the given route in the |
| * given buffer space. |
| * |
| * Returns the number of bytes written to the buffer. 0 or a negative |
| * value indicates an error. |
| */ |
| int |
| zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib, |
| uint8_t *in_buf, size_t in_buf_len) |
| { |
| Fpm__Message *msg; |
| QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096); |
| size_t len; |
| |
| QPB_INIT_STACK_ALLOCATOR (allocator); |
| |
| msg = create_route_message(&allocator, dest, rib); |
| if (!msg) { |
| assert(0); |
| return 0; |
| } |
| |
| len = fpm__message__pack(msg, (uint8_t *) in_buf); |
| assert(len <= in_buf_len); |
| |
| QPB_RESET_STACK_ALLOCATOR (allocator); |
| return len; |
| } |