1/* 2 * Code for encoding/decoding FPM messages that are in netlink format. 3 * 4 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro 5 * Copyright (C) 2012 by Open Source Routing. 6 * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") 7 * 8 * This file is part of GNU Zebra. 9 * 10 * GNU Zebra 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 * GNU Zebra 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 GNU Zebra; 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 26#include <zebra.h> 27 28#include "log.h" 29#include "rib.h" 30 31#include "rt_netlink.h" 32 33#include "zebra_fpm_private.h" 34 35/* 36 * addr_to_a 37 * 38 * Returns string representation of an address of the given AF. 39 */ 40static inline const char * 41addr_to_a (u_char af, void *addr) 42{ 43 if (!addr) 44 return "<No address>"; 45 46 switch (af) 47 { 48 49 case AF_INET: 50 return inet_ntoa (*((struct in_addr *) addr)); 51 52#ifdef HAVE_IPV6 53 case AF_INET6: 54 return inet6_ntoa (*((struct in6_addr *) addr)); 55#endif 56 57 default: 58 return "<Addr in unknown AF>"; 59 } 60} 61 62/* 63 * prefix_addr_to_a 64 * 65 * Convience wrapper that returns a human-readable string for the 66 * address in a prefix. 67 */ 68static const char * 69prefix_addr_to_a (struct prefix *prefix) 70{ 71 if (!prefix) 72 return "<No address>"; 73 74 return addr_to_a (prefix->family, &prefix->u.prefix); 75} 76 77/* 78 * af_addr_size 79 * 80 * The size of an address in a given address family. 81 */ 82static size_t 83af_addr_size (u_char af) 84{ 85 switch (af) 86 { 87 88 case AF_INET: 89 return 4; 90 91#ifdef HAVE_IPV6 92 case AF_INET6: 93 return 16; 94#endif 95 96 default: 97 assert(0); 98 return 16; 99 } 100} 101 102/* 103 * netlink_nh_info_t 104 * 105 * Holds information about a single nexthop for netlink. These info 106 * structures are transient and may contain pointers into rib 107 * data structures for convenience. 108 */ 109typedef struct netlink_nh_info_t_ 110{ 111 uint32_t if_index; 112 union g_addr *gateway; 113 114 /* 115 * Information from the struct nexthop from which this nh was 116 * derived. For debug purposes only. 117 */ 118 int recursive; 119 enum nexthop_types_t type; 120} netlink_nh_info_t; 121 122/* 123 * netlink_route_info_t 124 * 125 * A structure for holding information for a netlink route message. 126 */ 127typedef struct netlink_route_info_t_ 128{ 129 uint16_t nlmsg_type; 130 u_char rtm_type; 131 uint32_t rtm_table; 132 u_char rtm_protocol; 133 u_char af; 134 struct prefix *prefix; 135 uint32_t *metric; 136 int num_nhs; 137 138 /* 139 * Nexthop structures. We keep things simple for now by enforcing a 140 * maximum of 64 in case MULTIPATH_NUM is 0; 141 */ 142 netlink_nh_info_t nhs[MAX (MULTIPATH_NUM, 64)]; 143 union g_addr *pref_src; 144} netlink_route_info_t; 145 146/* 147 * netlink_route_info_add_nh 148 * 149 * Add information about the given nexthop to the given route info 150 * structure. 151 * 152 * Returns TRUE if a nexthop was added, FALSE otherwise. 153 */ 154static int 155netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop, 156 int recursive) 157{ 158 netlink_nh_info_t nhi; 159 union g_addr *src; 160 161 memset (&nhi, 0, sizeof (nhi)); 162 src = NULL; 163 164 if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs)) 165 return 0; 166 167 nhi.recursive = recursive; 168 nhi.type = nexthop->type; 169 nhi.if_index = nexthop->ifindex; 170 171 if (nexthop->type == NEXTHOP_TYPE_IPV4 172 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) 173 { 174 nhi.gateway = &nexthop->gate; 175 if (nexthop->src.ipv4.s_addr) 176 src = &nexthop->src; 177 } 178 179#ifdef HAVE_IPV6 180 if (nexthop->type == NEXTHOP_TYPE_IPV6 181 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME 182 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) 183 { 184 nhi.gateway = &nexthop->gate; 185 } 186#endif /* HAVE_IPV6 */ 187 188 if (nexthop->type == NEXTHOP_TYPE_IFINDEX 189 || nexthop->type == NEXTHOP_TYPE_IFNAME) 190 { 191 if (nexthop->src.ipv4.s_addr) 192 src = &nexthop->src; 193 } 194 195 if (!nhi.gateway && nhi.if_index == 0) 196 return 0; 197 198 /* 199 * We have a valid nhi. Copy the structure over to the route_info. 200 */ 201 ri->nhs[ri->num_nhs] = nhi; 202 ri->num_nhs++; 203 204 if (src && !ri->pref_src) 205 ri->pref_src = src; 206 207 return 1; 208} 209 210/* 211 * netlink_proto_from_route_type 212 */ 213static u_char 214netlink_proto_from_route_type (int type) 215{ 216 switch (type) 217 { 218 case ZEBRA_ROUTE_KERNEL: 219 case ZEBRA_ROUTE_CONNECT: 220 return RTPROT_KERNEL; 221 222 default: 223 return RTPROT_ZEBRA; 224 } 225} 226 227/* 228 * netlink_route_info_fill 229 * 230 * Fill out the route information object from the given route. 231 * 232 * Returns TRUE on success and FALSE on failure. 233 */ 234static int 235netlink_route_info_fill (netlink_route_info_t *ri, int cmd, 236 rib_dest_t *dest, struct rib *rib) 237{ 238 struct nexthop *nexthop, *tnexthop; 239 int recursing; 240 int discard; 241 242 memset (ri, 0, sizeof (*ri)); 243 244 ri->prefix = rib_dest_prefix (dest); 245 ri->af = rib_dest_af (dest); 246 247 ri->nlmsg_type = cmd; 248 ri->rtm_table = rib_dest_vrf (dest)->id; 249 ri->rtm_protocol = RTPROT_UNSPEC; 250 251 /* 252 * An RTM_DELROUTE need not be accompanied by any nexthops, 253 * particularly in our communication with the FPM. 254 */ 255 if (cmd == RTM_DELROUTE && !rib) 256 goto skip; 257 258 if (rib) 259 ri->rtm_protocol = netlink_proto_from_route_type (rib->type); 260 261 if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) 262 discard = 1; 263 else 264 discard = 0; 265 266 if (cmd == RTM_NEWROUTE) 267 { 268 if (discard) 269 { 270 if (rib->flags & ZEBRA_FLAG_BLACKHOLE) 271 ri->rtm_type = RTN_BLACKHOLE; 272 else if (rib->flags & ZEBRA_FLAG_REJECT) 273 ri->rtm_type = RTN_UNREACHABLE; 274 else 275 assert (0); 276 } 277 else 278 ri->rtm_type = RTN_UNICAST; 279 } 280 281 ri->metric = &rib->metric; 282 283 if (discard) 284 { 285 goto skip; 286 } 287 288 for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) 289 { 290 if (MULTIPATH_NUM != 0 && ri->num_nhs >= MULTIPATH_NUM) 291 break; 292 293 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) 294 continue; 295 296 if ((cmd == RTM_NEWROUTE 297 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) 298 || (cmd == RTM_DELROUTE 299 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) 300 { 301 netlink_route_info_add_nh (ri, nexthop, recursing); 302 } 303 } 304 305 /* If there is no useful nexthop then return. */ 306 if (ri->num_nhs == 0) 307 { 308 zfpm_debug ("netlink_encode_route(): No useful nexthop."); 309 return 0; 310 } 311 312 skip: 313 return 1; 314} 315 316/* 317 * netlink_route_info_encode 318 * 319 * Returns the number of bytes written to the buffer. 0 or a negative 320 * value indicates an error. 321 */ 322static int 323netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf, 324 size_t in_buf_len) 325{ 326 int bytelen; 327 int nexthop_num = 0; 328 size_t buf_offset; 329 netlink_nh_info_t *nhi; 330 331 struct 332 { 333 struct nlmsghdr n; 334 struct rtmsg r; 335 char buf[1]; 336 } *req; 337 338 req = (void *) in_buf; 339 340 buf_offset = ((char *) req->buf) - ((char *) req); 341 342 if (in_buf_len < buf_offset) { 343 assert(0); 344 return 0; 345 } 346 347 memset (req, 0, buf_offset); 348 349 bytelen = af_addr_size (ri->af); 350 351 req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); 352 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; 353 req->n.nlmsg_type = ri->nlmsg_type; 354 req->r.rtm_family = ri->af; 355 req->r.rtm_table = ri->rtm_table; 356 req->r.rtm_dst_len = ri->prefix->prefixlen; 357 req->r.rtm_protocol = ri->rtm_protocol; 358 req->r.rtm_scope = RT_SCOPE_UNIVERSE; 359 360 addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen); 361 362 req->r.rtm_type = ri->rtm_type; 363 364 /* Metric. */ 365 if (ri->metric) 366 addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); 367 368 if (ri->num_nhs == 0) 369 goto done; 370 371 if (ri->num_nhs == 1) 372 { 373 nhi = &ri->nhs[0]; 374 375 if (nhi->gateway) 376 { 377 addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway, 378 bytelen); 379 } 380 381 if (nhi->if_index) 382 { 383 addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index); 384 } 385 386 goto done; 387 388 } 389 390 /* 391 * Multipath case. 392 */ 393 char buf[NL_PKT_BUF_SIZE]; 394 struct rtattr *rta = (void *) buf; 395 struct rtnexthop *rtnh; 396 397 rta->rta_type = RTA_MULTIPATH; 398 rta->rta_len = RTA_LENGTH (0); 399 rtnh = RTA_DATA (rta); 400 401 for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) 402 { 403 nhi = &ri->nhs[nexthop_num]; 404 405 rtnh->rtnh_len = sizeof (*rtnh); 406 rtnh->rtnh_flags = 0; 407 rtnh->rtnh_hops = 0; 408 rtnh->rtnh_ifindex = 0; 409 rta->rta_len += rtnh->rtnh_len; 410 411 if (nhi->gateway) 412 { 413 rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen); 414 rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; 415 } 416 417 if (nhi->if_index) 418 { 419 rtnh->rtnh_ifindex = nhi->if_index; 420 } 421 422 rtnh = RTNH_NEXT (rtnh); 423 } 424 425 assert (rta->rta_len > RTA_LENGTH (0)); 426 addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta), 427 RTA_PAYLOAD (rta)); 428 429done: 430 431 if (ri->pref_src) 432 { 433 addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen); 434 } 435 436 assert (req->n.nlmsg_len < in_buf_len); 437 return req->n.nlmsg_len; 438} 439 440/* 441 * zfpm_log_route_info 442 * 443 * Helper function to log the information in a route_info structure. 444 */ 445static void 446zfpm_log_route_info (netlink_route_info_t *ri, const char *label) 447{ 448 netlink_nh_info_t *nhi; 449 int i; 450 451 zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label, 452 nl_msg_type_to_str (ri->nlmsg_type), 453 prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen, 454 nl_rtproto_to_str (ri->rtm_protocol), 455 ri->metric ? *ri->metric : 0); 456 457 for (i = 0; i < ri->num_nhs; i++) 458 { 459 nhi = &ri->nhs[i]; 460 zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", 461 nhi->if_index, addr_to_a (ri->af, nhi->gateway), 462 nhi->recursive ? "yes" : "no", 463 nexthop_type_to_str (nhi->type)); 464 } 465} 466 467/* 468 * zfpm_netlink_encode_route 469 * 470 * Create a netlink message corresponding to the given route in the 471 * given buffer space. 472 * 473 * Returns the number of bytes written to the buffer. 0 or a negative 474 * value indicates an error. 475 */ 476int 477zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, 478 char *in_buf, size_t in_buf_len) 479{ 480 netlink_route_info_t ri_space, *ri; 481 482 ri = &ri_space; 483 484 if (!netlink_route_info_fill (ri, cmd, dest, rib)) 485 return 0; 486 487 zfpm_log_route_info (ri, __FUNCTION__); 488 489 return netlink_route_info_encode (ri, in_buf, in_buf_len); 490} 491