| /* |
| * Code for encoding/decoding FPM messages that are in netlink format. |
| * |
| * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro |
| * Copyright (C) 2012 by Open Source Routing. |
| * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") |
| * |
| * This file is part of GNU Zebra. |
| * |
| * GNU Zebra 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. |
| * |
| * GNU Zebra 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 GNU Zebra; 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 "rt_netlink.h" |
| #include "nexthop.h" |
| |
| #include "zebra_fpm_private.h" |
| |
| /* |
| * addr_to_a |
| * |
| * Returns string representation of an address of the given AF. |
| */ |
| static inline const char * |
| addr_to_a (u_char af, void *addr) |
| { |
| if (!addr) |
| return "<No address>"; |
| |
| switch (af) |
| { |
| |
| case AF_INET: |
| return inet_ntoa (*((struct in_addr *) addr)); |
| |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| return inet6_ntoa (*((struct in6_addr *) addr)); |
| #endif |
| |
| default: |
| return "<Addr in unknown AF>"; |
| } |
| } |
| |
| /* |
| * prefix_addr_to_a |
| * |
| * Convience wrapper that returns a human-readable string for the |
| * address in a prefix. |
| */ |
| static const char * |
| prefix_addr_to_a (struct prefix *prefix) |
| { |
| if (!prefix) |
| return "<No address>"; |
| |
| return addr_to_a (prefix->family, &prefix->u.prefix); |
| } |
| |
| /* |
| * af_addr_size |
| * |
| * The size of an address in a given address family. |
| */ |
| static size_t |
| af_addr_size (u_char af) |
| { |
| switch (af) |
| { |
| |
| case AF_INET: |
| return 4; |
| |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| return 16; |
| #endif |
| |
| default: |
| assert(0); |
| return 16; |
| } |
| } |
| |
| /* |
| * netlink_nh_info_t |
| * |
| * Holds information about a single nexthop for netlink. These info |
| * structures are transient and may contain pointers into rib |
| * data structures for convenience. |
| */ |
| typedef struct netlink_nh_info_t_ |
| { |
| uint32_t if_index; |
| union g_addr *gateway; |
| |
| /* |
| * Information from the struct nexthop from which this nh was |
| * derived. For debug purposes only. |
| */ |
| int recursive; |
| enum nexthop_types_t type; |
| } netlink_nh_info_t; |
| |
| /* |
| * netlink_route_info_t |
| * |
| * A structure for holding information for a netlink route message. |
| */ |
| typedef struct netlink_route_info_t_ |
| { |
| uint16_t nlmsg_type; |
| u_char rtm_type; |
| uint32_t rtm_table; |
| u_char rtm_protocol; |
| u_char af; |
| struct prefix *prefix; |
| uint32_t *metric; |
| int num_nhs; |
| |
| /* |
| * Nexthop structures |
| */ |
| netlink_nh_info_t nhs[MULTIPATH_NUM]; |
| union g_addr *pref_src; |
| } netlink_route_info_t; |
| |
| /* |
| * netlink_route_info_add_nh |
| * |
| * Add information about the given nexthop to the given route info |
| * structure. |
| * |
| * Returns TRUE if a nexthop was added, FALSE otherwise. |
| */ |
| static int |
| netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop, |
| int recursive) |
| { |
| netlink_nh_info_t nhi; |
| union g_addr *src; |
| |
| memset (&nhi, 0, sizeof (nhi)); |
| src = NULL; |
| |
| if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs)) |
| return 0; |
| |
| nhi.recursive = recursive; |
| nhi.type = nexthop->type; |
| nhi.if_index = nexthop->ifindex; |
| |
| if (nexthop->type == NEXTHOP_TYPE_IPV4 |
| || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) |
| { |
| nhi.gateway = &nexthop->gate; |
| if (nexthop->src.ipv4.s_addr) |
| src = &nexthop->src; |
| } |
| |
| #ifdef HAVE_IPV6 |
| if (nexthop->type == NEXTHOP_TYPE_IPV6 |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME |
| || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) |
| { |
| nhi.gateway = &nexthop->gate; |
| } |
| #endif /* HAVE_IPV6 */ |
| |
| if (nexthop->type == NEXTHOP_TYPE_IFINDEX |
| || nexthop->type == NEXTHOP_TYPE_IFNAME) |
| { |
| if (nexthop->src.ipv4.s_addr) |
| src = &nexthop->src; |
| } |
| |
| if (!nhi.gateway && nhi.if_index == 0) |
| return 0; |
| |
| /* |
| * We have a valid nhi. Copy the structure over to the route_info. |
| */ |
| ri->nhs[ri->num_nhs] = nhi; |
| ri->num_nhs++; |
| |
| if (src && !ri->pref_src) |
| ri->pref_src = src; |
| |
| return 1; |
| } |
| |
| /* |
| * netlink_proto_from_route_type |
| */ |
| static u_char |
| netlink_proto_from_route_type (int type) |
| { |
| switch (type) |
| { |
| case ZEBRA_ROUTE_KERNEL: |
| case ZEBRA_ROUTE_CONNECT: |
| return RTPROT_KERNEL; |
| |
| default: |
| return RTPROT_ZEBRA; |
| } |
| } |
| |
| /* |
| * netlink_route_info_fill |
| * |
| * Fill out the route information object from the given route. |
| * |
| * Returns TRUE on success and FALSE on failure. |
| */ |
| static int |
| netlink_route_info_fill (netlink_route_info_t *ri, int cmd, |
| rib_dest_t *dest, struct rib *rib) |
| { |
| struct nexthop *nexthop, *tnexthop; |
| int recursing; |
| int discard; |
| |
| memset (ri, 0, sizeof (*ri)); |
| |
| ri->prefix = rib_dest_prefix (dest); |
| ri->af = rib_dest_af (dest); |
| |
| ri->nlmsg_type = cmd; |
| ri->rtm_table = rib_dest_vrf (dest)->vrf_id; |
| ri->rtm_protocol = RTPROT_UNSPEC; |
| |
| /* |
| * An RTM_DELROUTE need not be accompanied by any nexthops, |
| * particularly in our communication with the FPM. |
| */ |
| if (cmd == RTM_DELROUTE) |
| goto skip; |
| |
| if (!rib) |
| { |
| zlog_err("netlink_route_info_fill RTM_ADDROUTE called without rib info"); |
| return 0; |
| } |
| |
| ri->rtm_protocol = netlink_proto_from_route_type (rib->type); |
| |
| if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) |
| discard = 1; |
| else |
| discard = 0; |
| |
| if (cmd == RTM_NEWROUTE) |
| { |
| if (discard) |
| { |
| if (rib->flags & ZEBRA_FLAG_BLACKHOLE) |
| ri->rtm_type = RTN_BLACKHOLE; |
| else if (rib->flags & ZEBRA_FLAG_REJECT) |
| ri->rtm_type = RTN_UNREACHABLE; |
| else |
| assert (0); |
| } |
| else |
| ri->rtm_type = RTN_UNICAST; |
| } |
| |
| ri->metric = &rib->metric; |
| |
| if (discard) |
| { |
| goto skip; |
| } |
| |
| for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) |
| { |
| if (ri->num_nhs >= MULTIPATH_NUM) |
| break; |
| |
| if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) |
| continue; |
| |
| if ((cmd == RTM_NEWROUTE |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) |
| || (cmd == RTM_DELROUTE |
| && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) |
| { |
| netlink_route_info_add_nh (ri, nexthop, recursing); |
| } |
| } |
| |
| /* If there is no useful nexthop then return. */ |
| if (ri->num_nhs == 0) |
| { |
| zfpm_debug ("netlink_encode_route(): No useful nexthop."); |
| return 0; |
| } |
| |
| skip: |
| return 1; |
| } |
| |
| /* |
| * netlink_route_info_encode |
| * |
| * Returns the number of bytes written to the buffer. 0 or a negative |
| * value indicates an error. |
| */ |
| static int |
| netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf, |
| size_t in_buf_len) |
| { |
| size_t bytelen; |
| int nexthop_num = 0; |
| size_t buf_offset; |
| netlink_nh_info_t *nhi; |
| |
| struct |
| { |
| struct nlmsghdr n; |
| struct rtmsg r; |
| char buf[1]; |
| } *req; |
| |
| req = (void *) in_buf; |
| |
| buf_offset = ((char *) req->buf) - ((char *) req); |
| |
| if (in_buf_len < buf_offset) { |
| assert(0); |
| return 0; |
| } |
| |
| memset (req, 0, buf_offset); |
| |
| bytelen = af_addr_size (ri->af); |
| |
| req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); |
| req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; |
| req->n.nlmsg_type = ri->nlmsg_type; |
| req->r.rtm_family = ri->af; |
| req->r.rtm_table = ri->rtm_table; |
| req->r.rtm_dst_len = ri->prefix->prefixlen; |
| req->r.rtm_protocol = ri->rtm_protocol; |
| req->r.rtm_scope = RT_SCOPE_UNIVERSE; |
| |
| addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen); |
| |
| req->r.rtm_type = ri->rtm_type; |
| |
| /* Metric. */ |
| if (ri->metric) |
| addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); |
| |
| if (ri->num_nhs == 0) |
| goto done; |
| |
| if (ri->num_nhs == 1) |
| { |
| nhi = &ri->nhs[0]; |
| |
| if (nhi->gateway) |
| { |
| addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway, |
| bytelen); |
| } |
| |
| if (nhi->if_index) |
| { |
| addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index); |
| } |
| |
| goto done; |
| |
| } |
| |
| /* |
| * Multipath case. |
| */ |
| char buf[NL_PKT_BUF_SIZE]; |
| struct rtattr *rta = (void *) buf; |
| struct rtnexthop *rtnh; |
| |
| rta->rta_type = RTA_MULTIPATH; |
| rta->rta_len = RTA_LENGTH (0); |
| rtnh = RTA_DATA (rta); |
| |
| for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) |
| { |
| nhi = &ri->nhs[nexthop_num]; |
| |
| rtnh->rtnh_len = sizeof (*rtnh); |
| rtnh->rtnh_flags = 0; |
| rtnh->rtnh_hops = 0; |
| rtnh->rtnh_ifindex = 0; |
| rta->rta_len += rtnh->rtnh_len; |
| |
| if (nhi->gateway) |
| { |
| rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen); |
| rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; |
| } |
| |
| if (nhi->if_index) |
| { |
| rtnh->rtnh_ifindex = nhi->if_index; |
| } |
| |
| rtnh = RTNH_NEXT (rtnh); |
| } |
| |
| assert (rta->rta_len > RTA_LENGTH (0)); |
| addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta), |
| RTA_PAYLOAD (rta)); |
| |
| done: |
| |
| if (ri->pref_src) |
| { |
| addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen); |
| } |
| |
| assert (req->n.nlmsg_len < in_buf_len); |
| return req->n.nlmsg_len; |
| } |
| |
| /* |
| * zfpm_log_route_info |
| * |
| * Helper function to log the information in a route_info structure. |
| */ |
| static void |
| zfpm_log_route_info (netlink_route_info_t *ri, const char *label) |
| { |
| netlink_nh_info_t *nhi; |
| int i; |
| |
| zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label, |
| nl_msg_type_to_str (ri->nlmsg_type), |
| prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen, |
| nl_rtproto_to_str (ri->rtm_protocol), |
| ri->metric ? *ri->metric : 0); |
| |
| for (i = 0; i < ri->num_nhs; i++) |
| { |
| nhi = &ri->nhs[i]; |
| zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", |
| nhi->if_index, addr_to_a (ri->af, nhi->gateway), |
| nhi->recursive ? "yes" : "no", |
| nexthop_type_to_str (nhi->type)); |
| } |
| } |
| |
| /* |
| * zfpm_netlink_encode_route |
| * |
| * Create a netlink 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_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, |
| char *in_buf, size_t in_buf_len) |
| { |
| netlink_route_info_t ri_space, *ri; |
| |
| ri = &ri_space; |
| |
| if (!netlink_route_info_fill (ri, cmd, dest, rib)) |
| return 0; |
| |
| zfpm_log_route_info (ri, __FUNCTION__); |
| |
| return netlink_route_info_encode (ri, in_buf, in_buf_len); |
| } |